2
\$\begingroup\$

I was in a code review today and someone said, "Why don't you do an enum on that?" I had started to, but conforming to a bunch of protocols seemed like a gigantic PITA and it seemed more error prone than just writing something simple, readable, and maintainable.

That said, I started to fiddle around with it this evening and I can't figure it out.

This is what I've started with:

typealias PolicyType = (filename: String, text: String)

struct Policy {
  static let first = PolicyType(filename: "firstFile.txt", text: "text in first file")
  static let second = PolicyType(filename: "secondFile.txt", text: "text in second file")
  static let third = PolicyType(filename: "thirdFile.txt", text: "text in third file")
}

let thirdPolicyText = Policy.third.text

Is there a more memory efficient, maintainable way to do this with an enum? My primary objective is maintainability.

Below is what I've come up with. It feels hacky and looks like...well, you know:

public struct PolicyType : Equatable, ExpressibleByStringLiteral {
  var filename: String
  var text: String

  //MARK:- Equatable Methods
  public static func == (lhs: PolicyType, rhs: PolicyType) -> Bool {
    return (lhs.filename == rhs.filename && lhs.text == rhs.text)
  }

  //MARK:- ExpressibleByStringLiteral Methods
  public init(stringLiteral value: String) {
    let components = value.components(separatedBy: "|")
    self.filename = components[0]
    self.text = components[1]
  }

  public init(unicodeScalarLiteral value: String) {
    self.init(stringLiteral: value)
  }
  public init(extendedGraphemeClusterLiteral value: String) {
    self.init(stringLiteral: value)
  }
}

enum Policy: PolicyType {
  case first = "testFile1.txt|This is sample text"
  case second = "testFile2.txt|This is more sample text"
  case third = "testFile3.txt|This is more sample text"
}

Policy.third.rawValue.text
\$\endgroup\$
  • \$\begingroup\$ There is a difference between struct Policy and enum Policy: In the first case you have a type with 3 “predefined” values, but a program can create additional values, with arbitrary file names and texts. In the second case you have an enum with 3 values, and that's it. \$\endgroup\$ – Martin R Sep 21 '18 at 4:10
1
\$\begingroup\$
  1. The code is using Enum, but it's still using Struct.

  2. The program may be buggy if the text value has character |. Suppose that "testFile1.txt|This is sample text" to "testFile1.txt|This is | sample text".

I have another approach with Enum in this case:

 enum Policy {
    case first
    case second
    case third

    var fileName: String {
        switch self {
        case .first: return "testFile1.txt"
        case .second: return "testFile2.txt"
        case .third: return "testFile3.txt"
        }
    }

    var text: String {
        switch self {
            case .first : return "This is sample text"
            case .second: return "This is more sample text"
            case .third: return "This is more sample text"
        }
    }
}
   print(Policy.first.text)
\$\endgroup\$
  • \$\begingroup\$ Your squiggly brackets ({}) don't match up in your example. Where's the closing bracket for enum Policy {? \$\endgroup\$ – chicks Oct 1 '18 at 11:29
  • \$\begingroup\$ @chicks: I updated the example. I hope it more clearly \$\endgroup\$ – Kien Tran Oct 1 '18 at 14:34
1
\$\begingroup\$

First, I think your initial idea can be simplified a bit. No need for two separate types:

struct Policy: Equatable {
    static let first = Policy(filename: "firstFile.txt", text: "text in first file")
    static let second = Policy(filename: "secondFile.txt", text: "text in second file")
    static let third = Policy(filename: "thirdFile.txt", text: "text in third file")

    let filename: String
    let text: String

    private init(filename: String, text: String) {
        self.filename = filename
        self.text = text
    }
}

Note that making the init function private means that the only Policy objects that can exist are the three static lets (first, second and third). Also note that you don't need to implement the func ==, the compiler will do that automatically.

So what does it mean to make this an enum?

enum Policy: Equatable {
    case first
    case second
    case third

    var filename: String {
        switch self {
        case .first:
            return "firstFile.txt"
        case .second:
            return "secondFile.txt"
        case .third:
            return "thirdFile.txt"
        }
    }

    var text: String {
        switch self {
        case .first:
            return "text in first file"
        case .second:
            return "text in second file"
        case .third:
            return "text in third file"
        }
    }
}

Important note: Both the struct and the enum are used in the exact same way at the call site. You can freely switch between these two constructs without changing any other code anywhere else in the program.

So which is better? Since there is no difference in the way they are or can be used, then the only argument for or against either must be solely in the above. The struct solution is shorter and it's easier to add/remove/edit objects in it. And, if you want to be able to create objects that don't equal one of the basic values, you can do that with the struct by making the init internal/public, but you can't do it with the enum at all. By any standard I can think of, the struct is a superior solution.

Your reviewer asked, "Why don't you do an enum on that?" My response to her/him would be, "why should I? There's zero benefit to doing so."

Also, I agree with Kien Tran's answer. Making this type expressible by string literal is asking for problems.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.