#java #design-patterns
#java #шаблоны проектирования
Вопрос:
У меня есть 2 класса: X и Y. Ни один из них не связан или не имеет одинаковых свойств. Но есть другой класс Z, у которого есть установщики и получатели, где установщик может принимать тип X или Y, а получатель может возвращать тип X или Y. Я подумал о том, чтобы иметь родительский класс Parent для x, y и использовать его как тип для сеттеров и геттеров.. но я не думаю, что это хороший дизайн и нарушит весь смысл inheretence. Что я могу сделать в таком случае? Кроме использования универсального типа ‘T’ для класса z
Ответ №1:
Это хороший знак, что вам, вероятно, нужно переписать код, если метод иногда возвращает разные типы. Вы всегда должны возвращать один и тот же тип.
Обычно достаточно переставить код, но не всегда, и именно тогда обычно реализуется какой-то контейнерный объект, который содержит вложенные объекты исключительно с целью их возврата.
Ответ №2:
Отказ от ответственности
Как уже заявляли другие, это в первую очередь признак плохого дизайна и запахов.
Рассмотрите возможность перепроектирования вашего кода таким образом, чтобы у вас не было необходимости возвращать две несвязанные вещи одновременно.
Код
В любом случае, вот некоторый код, который бы реализовал решение для этой ситуации. Мы используем Container
класс, который предоставляет геттеры. Но работает только один из них, следовательно, существуют также методы проверки, какой из них он есть. Такой класс обычно называется Union
or Variant
, потому что разрешено содержать только один из типов.
public class Variant<X, Y> {
public static <X, Y> Variant<X, Y> ofX(X x) {
Objects.requireNonNull(x);
return new Variant(x, null);
}
public static <X, Y> Variant<X, Y> ofY(Y y) {
Objects.requireNonNull(y);
return new Variant(null, y);
}
private final X x;
private final Y y;
private Variant(X x, Y y) {
this.x = x;
this.y = y;
}
public X getX() {
if (x == null) {
throw new IllegalStateException("Contains the other type");
}
return x;
}
public Y getY() {
if (y == null) {
throw new IllegalStateException("Contains the other type");
}
return y;
}
public boolean isX() {
return x != null;
}
public boolean isY() {
return y != null;
}
}
Использование:
Variant<String, Integer> variant1 = Variant.ofX("hello");
System.out.println(variant1.getX()); // hello
Variant<String, Integer> variant2 = Variant.ofY(20);
System.out.println(variant2.getY()); // 20
Или в вашем конкретном примере:
public Variant<String, Integer> foo() {
...
if (...) {
return Variant.ofX("hello");
} else {
return Variant.ofY(20);
}
}
Примечания
Использование статических фабричных методов предназначено для того, чтобы иметь два разных конструктора с X x
и Y y
только для получения идентичной подписи после удаления типа. Таким образом, необходимы два разных имени метода.
В оде «Необязательно — мать всех байкшед» вы также можете подумать о переименовании getX()
и getY()
о чем-то, что звучит более серьезно, например getXOrThrow()
/ getYOrThrow()
.