#swift #enums #solid-principles #liskov-substitution-principle
#swift #перечисления #solid-принципы #принцип подстановки Лискова
Вопрос:
enum WeatherType {
case cloudy(coverage: Int)
case sunny
case windy
}
Я только что видел это в учебном пособии по Swift и не могу поверить, что они позволяют вам это делать. Теперь, всякий раз, когда я включаю это перечисление, я должен создать специальный случай для cloudy
!
Комментарии:
1. Как говорит Vaca ниже, хотя это очень интересное обсуждение, оно, похоже, не относится конкретно к «Принципу подстановки Лискова».
Ответ №1:
Вы ничего не «должны» делать. Если вам все равно, что такое покрытие, не спрашивайте, что такое покрытие. Если вам все равно, облачно ли, не спрашивайте, облачно ли. Нет ничего особенного в том, как вы пишете переключатель для случая, который имеет связанное значение.
Предположим, у нас есть это:
let weather = WeatherType.cloudy(coverage:1)
Тогда это совершенно законно:
switch weather {
case .sunny:
print("it is sunny")
default:
print("I guess it's cloudy, or maybe windy")
}
И это тоже!
switch weather {
case .cloudy:
print("it is cloudy")
default:
print("I guess it's sunny, or maybe windy")
}
И ни один закон не требует, чтобы вы вообще писали оператор switch. Если вы просто хотите узнать, есть ли weather
.cloudy
, просто спросите:
if case .cloudy = weather {
print("yes it is cloudy")
} else {
print("I guess it's sunny, or maybe windy")
}
И если вы действительно хотите узнать, что coverage
это такое, вам все равно не нужно писать оператор switch:
if case let .cloudy(cov) = weather {
print("yes it is cloudy, in fact it is (cov)")
} else {
print("I guess it's sunny, or maybe windy")
}
Чего вы не можете сделать, так это применить ==
. Это не будет компилироваться:
if weather == .sunny { // error
В этом смысле, да, перечисление со связанным значением ведет себя иначе, чем перечисление без связанного значения (если это то, что вы спрашиваете).
Комментарии:
1. Спасибо вам за это. Мне просто кажется странным, что все члены enum не используют один и тот же набор параметров. Это, конечно, не совсем LSP, но это аналогичная ситуация, когда взаимозаменяемые объекты имеют разное поведение.
2. Перечисление — это тип. Такая вещь, как
.cloudy
не является «членом»; это экземпляр. Разные экземпляры не могут иметь разные данные?3. Да, я думаю, что это нарушает LSP, разные экземпляры могут иметь разные данные, но в одном и том же формате. Если у вас есть перечисления со связанными типами, вы не можете работать с ними одинаково. Это может генерировать беспорядочный код и множество случаев исключения.
Ответ №2:
Теперь, всякий раз, когда я включаю это
enum
, я должен создать специальный случай дляcloudy
!
Это не так по нескольким причинам:
- Вы можете игнорировать
cloudy
, объединяя его сdefault
, и - Сопоставление с образцом позволяет считывать
coverage
свойствоcloudy
, рассматриваяcloudy
его как единичный случай. - Когда вы читаете свойство
coverage
, у вас есть возможность игнорировать его или действовать в соответствии с ним.
Вот несколько примеров:
switch weatherType {
case .Cloudy(_):
print("It is cloudy. I ignore the coverage.")
...
}
switch weatherType {
case .Cloudy(let coverage):
print("It is cloudy. Coverage is (coverage).")
...
}
switch weatherType {
case .Cloudy(let coverage) where coverage > 80:
print("It is very cloudy.")
case .Cloudy(let coverage) where coverage < 20:
print("It is slightly cloudy.")
case .Cloudy(_):
print("It is cloudy.")
...
}
Комментарии:
1. Я бы хотел увидеть комментарий к анонимному понижению здесь.
2. Анонимное голосование против — одна из моих наименее любимых функций Stack Overflow. Как вы должны решить проблему (если она действительно есть), не зная, почему человек проголосовал против? Я никогда не отклоняю ответ другого человека на вопрос, на который я ответил, но я голосую за других, как я только что сделал здесь.
3. Спасибо вам за это. Мне просто кажется странным, что все члены enum не используют один и тот же набор параметров. Это, конечно, не совсем LSP, но это аналогичная ситуация, когда взаимозаменяемые объекты имеют разное поведение.
Ответ №3:
Принцип подстановки Лискова связан с подклассами. Перечисления не допускают подклассов, поэтому здесь это неприменимо.
Я не уверен, в чем заключается ваше возражение по поводу создания специального случая cloudy
; вы должны сделать это в любом случае, switch
если хотите конкретно обработать этот случай. Дополнительный синтаксис для захвата связанного значения довольно прост.
let weather = WeatherType.cloudy(coverage: 17)
switch weather {
case .sunny:
print("sunny")
case .windy:
print("windy")
case .cloudy(let coverage):
print("cloudy with coverage (coverage)")
}
Комментарии:
1. Спасибо вам за это. Мне просто кажется странным, что все члены enum не используют один и тот же набор параметров. Это, конечно, не совсем LSP, но это аналогичная ситуация, когда взаимозаменяемые объекты имеют разное поведение.
2. Я могу понять, что это кажется немного странным, но это очень полезно. На самом деле, опции — это просто перечисления под капотом.
Optional<Int>
имеет 2 значения перечисления:.none
которое представляетnil
и.some(Int)
. Таким образом, разворачивание необязательного значения на самом деле просто извлекает связанное значение из.some
регистра.