Проблема с дизайном с использованием шаблона декоратора
Итак, это мой дизайн. AccessDecorator
классы имеют ссылку на другой Access
так же, как обычный шаблон декоратора.
Но проблема в том, когда я создаю AccessDecorator
обертывание ConcreteAccess
а затем попробуйте увидеть, какой тип доступа:
Access access = new InAllowedAccess();
Access wrapperAccess = new MismatchAccess(access);
if (wrapperAccess instanceof InAllowedAccess) //this condition could be used to be a predicate for a filtering over an access list for example
//do something
Конечно, это не сработает, потому что wrapperAccess
не тип InAllowedAccess
но то, что я действительно хочу знать, это тип конкретного доступа, завернутого декоратором.
Я думал о реализации таких методов, как isInstanceofInAllowed()
, isInstanceofOutAllowed()
, isInstanceofInDenied()
а также isinstanceofOutDeniedd()
в Access
классы, но не кажется хорошим решением, я не знаю...
В противном случае я должен создать классы декоратора для каждых 4 типов InAllowedAccess
, OutAllowedAccess
, InDeniedAccess
а также OutDeniedAccess
?
Или есть другой лучший дизайн?
2 ответа
Как правило, избегать проверки типов - лучший способ сделать что-то. К сожалению, вы не дали достаточно контекста, как вы собираетесь использовать свои классы, чтобы я мог привести пример того, как вы можете использовать полиморфизм и избежать его.
Добавление проверки типов ограничит возможности вашей системы расти, потому что по мере добавления новых классов эти типы должны быть включены в проверки типов. Иногда это может привести к ошибкам, так как ваш код может делать предположения о количестве классов или их типов. Вот пример:
Примечание: я только что сделал это для наглядности. Дело не в том, чтобы представлять вашу логику или что-то в этом роде.
public void someMethod(Access access) {
if(access instance of InAccess) {
InAccess inAccess = (InAccess)access;
}
else {
OutAccess outAccess = (OutAccess)access;
}
}
Когда мы начали нашу систему было два класса, которые наследуют от Access
, Предположим, что мы добавляем еще Access class
в нашей системе. Этот код потерпит неудачу, поскольку мы можем передать новый третий тип доступа, и приведение не будет выполнено успешно.
Конечно, это не всегда так. Иногда количество ваших классов не будет слишком сильно расти. Вполне возможно, что вы можете предсказать все типы, которые будут иметь.
И, конечно, поскольку все может происходить в программировании, иногда вам нужно знать типы объектов, которые вы используете.
Давайте предположим, что вашей системе нужно знать тип объектов. Вот два решения:
- Добавьте перечисление, которое будет представлять все типы, которые у вас есть.
public enum AccessType {
InAccessAllowed,
InAccessDenied,
OutAccessDenied,
// other types
}
public interface Access {
AccessType getType();
// other stuff
}
Таким образом, вы будете использовать enum AccessType
вместо литья типа.
- Используйте интерфейсы.
Вместо использования классов определите интерфейс для каждого типа Access
, Затем вы будете проверять интерфейсы вместо классов. Таким образом, ваши декораторы могут реализовать тот же интерфейс, что и класс, который он декорирует.
public interface InAllowedAccess { }
public class InAllowedAccessImp implements InAllowedAccess { }
public class InAllowedAccessDecorator implements InAllowedAccess { }
Я просто не хочу приводить пример альтернативных реализаций. Поскольку в вашем описании отсутствует контекст, я просто попытаюсь угадать, как вы собираетесь использовать свои классы, и добавить к ним поведение. Это просто идея и ничего более.
Предположим, что ваша система предоставляет доступ пользователям. Пользователям могут быть предоставлены входные и исходящие права доступа, и некоторая часть вашей системы должна спросить, разрешен ли доступ конкретному пользователю, чтобы он мог выполнять определенную логику.
Если у вас нет никакого поведения, связанного с вашим Access classes
Вы можете просто использовать его как дескриптор, который будет нести информацию, необходимую для других классов, чтобы выполнять свою работу.
public enum PortType { In, Out }
public enum Permissions { Allowed, Denied }
public class Access {
private PortType mPortType;
private Permissions mPermissions;
public Access(PortType portType, Permissons permissions) {
mPortType = portType;
mPermissions = permissions;
}
public PortType getType() { return mPortType; }
public Permissions getPermissions() { return mPermissions; }
}
Если у вас есть поведение, то вы можете использовать полиморфизм. Определите поведение в вашем Access interface
и пусть классы, которые управляют этим интерфейсом, определяют поведение.
Допустим, у нас есть система обмена сообщениями, которая позволяет пользователю получать (входить) и отправлять (выводить) сообщения. Эти сообщения проходят через канал. Эти каналы будут либо принимать, либо отклонять сообщения. Вот способ, которым вы можете использовать полиморфизм вместо проверки типов.
public interface MessageChannel {
public bool canSendMessages(); // out
public bool canReceiveMessages(); // in
public void receiveMessage(Message m);
public void sendMessage(Message m);
}
public class InMessageChannel implements MessageChannel {
// out messaging is not allowed, cannot send
public bool canSendMessages() { return false; }
// In messaging allowed, can receive
public bool canReceiveMessages() { return true; }
public void sendMessage(Message m) {
throw new SendingMessagesIsNotAllowd();
}
public void receiveMessage(Message m); {
// do something with the mssage
}
}
public class OutMessageChannel implements MessageChannel {
// out messaging allowed
public bool canSendMessages() { return true; }
// In messaging not allowed
public bool canReceiveMessages() { return false; }
public void sendMessage(Message m) {
// send the message
}
public void receiveMessage(Message m); {
throw new ReceivingMessagesIsNotAllowd();
}
}
Как вы можете видеть каждый MessageCahnnel
имеет поведение, связанное с этим. Он может отправлять или получать сообщения, если это разрешено или нет. Таким образом, другие классы, которые используют MessageChannel
не нужно делать приведение типов.
Я думал о реализации методов, таких как isInstanceofInAllowed(), isInstanceofOutAllowed(), isInstanceofInDenied() и isinstanceofOutDeniedd() в классах Access, но не кажется хорошим решением, я не знаю...
Вы правы. Это плохое решение. Интерфейс часто относится к уровню с высоким уровнем абстракции в программном обеспечении, поэтому список его методов должен быть стабильным. Если вы положите такую кучу методов, как выше, в Access
интерфейс, интерфейс будет очень нестабильным, так как в будущем очень вероятно, что вы добавите больше таких методов к нему.
Самое простое решение вашей проблемы - добавление (только один раз) нового метода с именем core()
к Access
интерфейс. Каждый декоратор просто реализует этот метод, возвращая обернутый / основной объект.
interface Access {
...
Access core();
}
Access a = ...
if (a.core() instanceof ...