Skip to main content
added 2 characters in body
Source Link
user34073
user34073

This is a better approach than your previous answerquestion, but let's make it even better, shall we? Let's take a protocol-oriented approach to this problem.

What methods does the HangmanWordGenerator implement that the HangmanBrain actually need? Realistically, only generateWord, right?

So let's make a protocol and have the HangmanBrain have it's generator's type be this protocol, shall we?

protocol HangmanWordGenerator {
    func nextWord() -> String?
}

Now, in our HangmanBrain, this is our type:

var wordGenerator: HangmanWordGenerator?

So now, we can just make classes or structs that implement this protocol to use as our generator. So perhaps we rename your current generator class as something marking at it as a JSON word generator?

class JSONWordGenerator : HangmanWordGenerator { // etc...

Now later, I could extend your game by creating my own class that perhaps grabs words from some online dictionary...

class OnlineDictionaryWordGenerator : HangmanWordGenerator { // etc...

All your HangmanBrain class needs is a means for getting a word to play (by way of nextWord() that the protocol requires). By using a protocol rather than a class, you leave 100% of the implementation details up to whomever is extending the game with this new means of generating a word.

They don't have to inherit any of the code you've written.


init() {
    //not sure what to do here? without an empty init xcode says I have to use the one below
}

init(word: String) {
    self.word = word //when the user inputs a word
}

These constructors don't really make sense.

When we're constructing a brain, we don't immediately need a word. What we need is a generator. We'll only ask our generator for a word whenever it's time to start a game.

It also probably makes sense to grab a difficulty level during the constructor. So we want something like this:

init(generator: HangmanWordGenerator, difficulty: DifficultyLevel)

switch level.rawValue {
case "Easy": guesses = 10
case "Medium": guesses = 8
case "Hard": guesses = 6
default: break
}

It's not even entirely necessary for our enum to be String backed. But the place to use the backing data isn't in our switches. This is entirely inefficient and make matching more difficult. Instead, just switch on the value without expanding it to its raw value.

switch level {
case .Easy: guesses = 10
case .Medium: guesses = 8
case .Hard: guesses = 6
}

Doing it this way also means we're not required to have a default case.

This is a better approach than your previous answer, but let's make it even better, shall we? Let's take a protocol-oriented approach to this problem.

What methods does the HangmanWordGenerator implement that the HangmanBrain actually need? Realistically, only generateWord, right?

So let's make a protocol and have the HangmanBrain have it's generator's type be this protocol, shall we?

protocol HangmanWordGenerator {
    func nextWord() -> String?
}

Now, in our HangmanBrain, this is our type:

var wordGenerator: HangmanWordGenerator?

So now, we can just make classes or structs that implement this protocol to use as our generator. So perhaps we rename your current generator class as something marking at it as a JSON word generator?

class JSONWordGenerator : HangmanWordGenerator { // etc...

Now later, I could extend your game by creating my own class that perhaps grabs words from some online dictionary...

class OnlineDictionaryWordGenerator : HangmanWordGenerator { // etc...

All your HangmanBrain class needs is a means for getting a word to play (by way of nextWord() that the protocol requires). By using a protocol rather than a class, you leave 100% of the implementation details up to whomever is extending the game with this new means of generating a word.

They don't have to inherit any of the code you've written.


init() {
    //not sure what to do here? without an empty init xcode says I have to use the one below
}

init(word: String) {
    self.word = word //when the user inputs a word
}

These constructors don't really make sense.

When we're constructing a brain, we don't immediately need a word. What we need is a generator. We'll only ask our generator for a word whenever it's time to start a game.

It also probably makes sense to grab a difficulty level during the constructor. So we want something like this:

init(generator: HangmanWordGenerator, difficulty: DifficultyLevel)

switch level.rawValue {
case "Easy": guesses = 10
case "Medium": guesses = 8
case "Hard": guesses = 6
default: break
}

It's not even entirely necessary for our enum to be String backed. But the place to use the backing data isn't in our switches. This is entirely inefficient and make matching more difficult. Instead, just switch on the value without expanding it to its raw value.

switch level {
case .Easy: guesses = 10
case .Medium: guesses = 8
case .Hard: guesses = 6
}

Doing it this way also means we're not required to have a default case.

This is a better approach than your previous question, but let's make it even better, shall we? Let's take a protocol-oriented approach to this problem.

What methods does the HangmanWordGenerator implement that the HangmanBrain actually need? Realistically, only generateWord, right?

So let's make a protocol and have the HangmanBrain have it's generator's type be this protocol, shall we?

protocol HangmanWordGenerator {
    func nextWord() -> String?
}

Now, in our HangmanBrain, this is our type:

var wordGenerator: HangmanWordGenerator?

So now, we can just make classes or structs that implement this protocol to use as our generator. So perhaps we rename your current generator class as something marking at it as a JSON word generator?

class JSONWordGenerator : HangmanWordGenerator { // etc...

Now later, I could extend your game by creating my own class that perhaps grabs words from some online dictionary...

class OnlineDictionaryWordGenerator : HangmanWordGenerator { // etc...

All your HangmanBrain class needs is a means for getting a word to play (by way of nextWord() that the protocol requires). By using a protocol rather than a class, you leave 100% of the implementation details up to whomever is extending the game with this new means of generating a word.

They don't have to inherit any of the code you've written.


init() {
    //not sure what to do here? without an empty init xcode says I have to use the one below
}

init(word: String) {
    self.word = word //when the user inputs a word
}

These constructors don't really make sense.

When we're constructing a brain, we don't immediately need a word. What we need is a generator. We'll only ask our generator for a word whenever it's time to start a game.

It also probably makes sense to grab a difficulty level during the constructor. So we want something like this:

init(generator: HangmanWordGenerator, difficulty: DifficultyLevel)

switch level.rawValue {
case "Easy": guesses = 10
case "Medium": guesses = 8
case "Hard": guesses = 6
default: break
}

It's not even entirely necessary for our enum to be String backed. But the place to use the backing data isn't in our switches. This is entirely inefficient and make matching more difficult. Instead, just switch on the value without expanding it to its raw value.

switch level {
case .Easy: guesses = 10
case .Medium: guesses = 8
case .Hard: guesses = 6
}

Doing it this way also means we're not required to have a default case.

Source Link
nhgrif
  • 25.4k
  • 3
  • 64
  • 129

This is a better approach than your previous answer, but let's make it even better, shall we? Let's take a protocol-oriented approach to this problem.

What methods does the HangmanWordGenerator implement that the HangmanBrain actually need? Realistically, only generateWord, right?

So let's make a protocol and have the HangmanBrain have it's generator's type be this protocol, shall we?

protocol HangmanWordGenerator {
    func nextWord() -> String?
}

Now, in our HangmanBrain, this is our type:

var wordGenerator: HangmanWordGenerator?

So now, we can just make classes or structs that implement this protocol to use as our generator. So perhaps we rename your current generator class as something marking at it as a JSON word generator?

class JSONWordGenerator : HangmanWordGenerator { // etc...

Now later, I could extend your game by creating my own class that perhaps grabs words from some online dictionary...

class OnlineDictionaryWordGenerator : HangmanWordGenerator { // etc...

All your HangmanBrain class needs is a means for getting a word to play (by way of nextWord() that the protocol requires). By using a protocol rather than a class, you leave 100% of the implementation details up to whomever is extending the game with this new means of generating a word.

They don't have to inherit any of the code you've written.


init() {
    //not sure what to do here? without an empty init xcode says I have to use the one below
}

init(word: String) {
    self.word = word //when the user inputs a word
}

These constructors don't really make sense.

When we're constructing a brain, we don't immediately need a word. What we need is a generator. We'll only ask our generator for a word whenever it's time to start a game.

It also probably makes sense to grab a difficulty level during the constructor. So we want something like this:

init(generator: HangmanWordGenerator, difficulty: DifficultyLevel)

switch level.rawValue {
case "Easy": guesses = 10
case "Medium": guesses = 8
case "Hard": guesses = 6
default: break
}

It's not even entirely necessary for our enum to be String backed. But the place to use the backing data isn't in our switches. This is entirely inefficient and make matching more difficult. Instead, just switch on the value without expanding it to its raw value.

switch level {
case .Easy: guesses = 10
case .Medium: guesses = 8
case .Hard: guesses = 6
}

Doing it this way also means we're not required to have a default case.