В этой гипотетической задаче я создаю класс для представления колбасы, который может быть: A. Свежий и упакованный B. Свежий и неупакованный C. Не свежие и не упакованные
Примечание: он не может быть не свежим и не упакованным.
Я ищу лучшую практику для создания конструктора с учетом этого.
Способ, которым я пытался, приведен ниже, но я думаю, что должны быть лучшие решения.
public class Sausage {
Meat meat;
boolean isFresh;
boolean isPackaged;
public Sausage(Meat meat, Boolean isFresh, Boolean isPackaged) {
this.meat = meat;
if (!isFresh) {
this.isFresh = false;
this.isPackaged = false;
}
else if (isPackaged) {
this.isFresh = true;
this.isPackaged = true;
}
else {
this.isFresh = true;
this.isPackaged = false;
}
}
}
Я ищу более чистый способ обеспечить эту функциональность.
Есть несколько проблем с вашим текущим подходом:
Существует способ создать объект записи new
, который не имеет смысла, например. new Sausage(..., false, true)
, где упаковано, будет автоматически преобразовано в false
. Это сбивает с толку и затрудняет понимание кода.
Вы смешиваете Boolean
тип объекта и boolean
примитивный тип, что приводит к ненужной автоматической упаковке. Если вам не нужно поддерживать null
, используйте boolean
.
boolean
поля, вероятно, не нуждаются в префиксе is
.
Поскольку есть два логических флага и 4 возможности, которые они могут быть установлены (true true
, true false
, false true
, false false
), но только 3 допустимых варианта, возможно, с именами фабричных методов были бы более подходящими:
public class Sausage {
public static Sasuage newFreshPackaged(Meat meat) {
return new Sasuage(meat, true, true);
}
public static Sasuage newFreshNotPackaged(Meat meat) {
return new Sasuage(meat, true, false);
}
public static Sasuage newNotFreshNotPackaged(Meat meat) {
return new Sasuage(meat, false, false);
}
private Sausage(Meat meat, boolean fresh, boolean packaged) {
this.meat = meat;
this.fresh = fresh;
this.packaged = packaged;
}
}
Автоматическое изменение isPackaged
на false
может стать неожиданностью для вызывающего кода. Здесь вы можете выбрать исключение, если выбрана комбинация (не свежая, упакованная).
if (!isFresh && isPackaged) {
throw new IllegalArgumentException("Can't be both not fresh and packaged!");
}
this.isFresh = isFresh;
this.isPackaged = isPackaged;
Вы также можете решить использовать фабричные методы (создав свой конструктор private
), которые обеспечивают выполнение ваших требований. Это позволит избежать необходимости создания исключения.
public static Sausage createFreshSausage(Meat meat, boolean isPackaged) {
return new Sausage(meat, true, isPackaged);
}
public static Sausage createNotFreshSausage(Meat meat) {
return new Sausage(meat, false, false);
}
Между прочим, обычно примитив boolean
будет использоваться в типах параметров конструктора, а не в оболочке объекта Boolean
, как ваши переменные экземпляра. Значение null
, которое было бы разрешено Boolean
, не имеет никакого смысла.
Вы можете представить возможные состояния как тип перечисления:
enum SausageType {
FreshPackaged,
FreshUnpackaged,
NotFresh;
}
Затем измените свой конструктор, чтобы он принимал SausageType
вместо двух Boolean
.
Преимущество этого метода в том, что невозможно вызвать конструктор с неправильной комбинацией состояний, пользователям вашего класса очевидно, каковы возможные значения, и вам не нужно помнить о хрупкой проверке аргументов во время выполнения. в курсе меняющихся требований.
Отлично. Проблема ОП на самом деле не в том, чтобы «обработать все варианты всех различных логических условий». На самом деле это «определение« состояния »». Таким образом, перечисление является решением ИДЕАЛ.
@ paulsm4: идеальность решения зависит от того, как будет использоваться остальная часть кода. Поскольку это теоретический вопрос, давайте предположим сценарий, в котором вы получаете свежее и упакованное состояние откуда-то еще (например, из некоторых внешних служб). В этом случае вам все равно понадобится фабрика для преобразования различных флагов в перечисление, поэтому может быть более подходящим использовать фабрику в первую очередь.
@second Совершенно верно - общая применимость моего ответа будет зависеть от того, как выглядит остальная часть кода.
во-вторых, утверждается, что рассмотрение «enum» в качестве решения может быть преждевременным, если вы не знаете больше о приложении. Я не могу не согласиться. Но болтать о "фабриках" еще БОЛЕЕ "преждевременно" по той же причине. Я просто боялся, что ОП может быть «заблокирован» и думать исключительно о логических значениях и блоках if/else. Альтернативная точка зрения Браннана Винсента - рассмотрение "состояния" вместо "жонглирования булевыми значениями" была чрезвычайно полезной. ПО МОЕМУ МНЕНИЮ :)
Спасибо за это решение. Прочитав ваши комментарии, это будет решение, которое я реализую. Я обобщил проблему, однако контекст создает библиотеку, поэтому это решение прекрасно работает. Я не был знаком с enum. Спасибо вам всем.
По какой причине вы используете объекты
Boolean
в качестве параметра вместо их примитивного аналога (который вы используете внутри)?