Прежде всего, я не вижу причин не позволять создавать экземпляры ваших классов напрямую. Обычно вам все равно, кто создал экземпляр класса, если вы уверены, что он был создан правильно.
Поэтому, я считаю, у вас есть не одна проблема, а два:
- обеспечить способ для тех, кто правильно создавать как класс
A
и класс B
вручную, если это необходимо.
- Предоставьте способ создания экземпляра либо
A
, либо B
, учитывая некоторый набор абстрактных параметров (ваше решение для Factory).
Что касается первой части, то есть 3 способа правильно создания экземпляров классов различной сложности:
- конструктор, который содержит список всех необходимых параметров и зависимостей. Это очень удобно использовать для простых случаев.
- Метод завода. Это можно использовать для более сложных сценариев.
- Заводской класс/класс строителя. Они обычно используются для сложных сценариев.
Теперь, какой бы вы ни выбрали, по всей логике должно быть разрешено быть общедоступным. Конструктор/фабричный метод/фабричный класс будут применять ваши правила для создания правильного действительного экземпляра либо A
, либо B
. И, как я уже упоминал ранее, нет возможного сценария, когда вы должны запретить создание совершенно хорошего и действительного экземпляра класса.
Предположим, вы пошли с классом строителя как наиболее сложное решение. Вот как ваш код может выглядеть:
package com.app.letter.A;
public class A {
A() { //Package visibility, we don't want anyone to create an invalid A class
...
}
...
}
public class ABuilder {
public void validateAndSetSomeCriticalParam(Param param) {
...
}
public A build() {
A a = new A();
a.setSomeCriticalParam(param);
...
return a;
}
}
Застройщик должен быть разработан в мысли в виду, что он не может каким-либо образом произвести недопустимую экземпляр A
. Таким образом, вы можете позволить строителю быть единственным способом создания экземпляра A
и не беспокоиться об этом, потому что все созданные им экземпляры всегда действительны. Вы можете использовать правильный API для строителя или Исключения, чтобы достичь этого. Кроме того, подход строителя является самым сложным, для некоторых более простых сценариев вы можете использовать кучу общедоступных статических методов фабрики. Однако идея должна оставаться прежней - методы общественного завода должны гарантировать, что они производят только действительные экземпляры A
.
же материал для класса B, в другом пакете:
package com.app.letter.B;
public class B {
...
}
public class BBuilder {
...
}
Сейчас на заводе. В принципе то же самое, что вы имели, но со строителями:
package com.app.letter;
public class LetterFactory{
public static LetterChange getInstance(Object doesNotMatter){
if (doesNotMatter.isA()) {
ABuilder builder = new ABuilder();
builder.setSomeCriticalParam(...);
builder...
return builder.build();
} else {
BBuilder builder = new BBuilder();
builder.setSomeBSpecificParam(...);
builder...
return builder.build();
}
}
}
И о использований:
public class DoesNotMatterClass{
public void situations(){
LetterFactory.getInstance(..whatever..); // Legal
new A(); //Illegal, as it is package protected
new B(); //Illegal, as it is package protected
new ABuilder(); //Legal, as ABuilder can ensure that only valid As are created
new BBuilder(); //Legal, as BBuilder can ensure that only valid Bs are created
}
}
Я добавлю, повторяюсь еще раз, вы должны скрывать только части системы, которые могут быть злоупотребляли в некотором смысле. Нет смысла скрывать класс или метод, если нет возможности злоупотреблять им. Поэтому, если вы предоставили способ правильно инициализировать действительный экземпляр A
или B
, вам не важно, чтобы какая-то другая часть системы могла его увидеть или использовать.
Интересно, что вы беспокоитесь о том, что классы в одном пакете совершают незаконные действия. В некотором смысле это похоже на код в классе, доступ к частным членам класса, а не через общедоступные методы. Обычно мы скрываем вещи, потому что мы не хотим, чтобы код сломался, если мы изменим их. Ожидаете ли вы, что разработчики в пакете получат доступ к A и B, не зная рисков? Кроме того, я думаю, вы могли бы сделать это с помощью модуля в Java 9, если эта функция сделает его. – Fuhrmanator