У меня есть эти рабочие классы, выполняющие некоторую работу:
class WorkerOne implements IWorker {
@Override
public doWork(Resource resource){
// do some work
}
}
Я хочу, чтобы все методы doWork() были обернуты одной и той же операцией.
Один из способов добиться этого — создать абстрактный класс, как в следующем примере:
abstract class WorkWrapper implements IWorker {
@Override
public doBetterWork(){
Resource resource = // ...
doWork(resource)
resource .close();
}
}
class WorkerOne extends WorkWrapper {
protected doWork(Resource resource){
// do some work
}
}
и вызовите Worker как:
WorkerOne worker = new WorkerOne();
worker.doBetterWork();
По некоторым причинам я предпочитаю не использовать наследование. Есть ли лучшее решение для этой упаковки?




С помощью метода default, который вы можете определить в интерфейсах, вы можете избежать наследования (extends) и придерживаться реализации (implements):
class WorkerOne implements IWorker {
@Override
public void doWork(Resource resource){
// do some work
}
}
public interface IWorker {
void doWork(Resource resource);
default void doBetterWork(){
Resource resource = // ...
doWork(resource)
reource.close();
}
}
И используйте его, как вы это делали ранее:
IWorker worker = new WorkerOne();
worker.doBetterWork();
I want all doWork() methods to be wrapped by the same operation.
Лично мне не очень нравятся дизайны, в которых я выставляю два метода с точки зрения API, в то время как только один из них должен вызываться клиентами класса. Это вводит в заблуждение.
Чтобы избежать этого, я бы, вероятно, использовал композицию и декоратор (не обычный декоратор, поскольку сигнатура между двумя методами doWork() различается).
public interface IWorker {
void doWork(Resource resource);
}
class WorkWrapper{
private IWorker decorated;
public WorkWrapper(IWorker decorated){
this.decorated = decorated;
}
@Override
public doWork(){
Resource resource = // ...
decorated.doWork(resource);
reource.close();
}
}
class FooWork implements IWorker {
@Override
public doWork(Resource resource){
// do something...
}
}
Теперь двусмысленности быть не может:
WorkWrapper worker = new WorkWrapper(new FooWork());
worker.doWork(); // just this method is exposed now in WorkWrapper
Вы можете объединить его с фабрикой, чтобы упростить задачу и скрыть детали реализации со стороны клиента:
WorkWrapper worker = FooWork.createWrapperFor();
worker.doWork();
Ситуация, которую вы описываете, кажется, является точным контекстом, в котором можно было бы применить шаблон проектирования Метод шаблона. Однако эти шаблоны действительно требуют наследования.
Есть альтернативы. Тем не менее, это настолько хорошо подходит для ваших требований, что я бы не стал называть их «лучшими» решениями.
Очевидной альтернативой наследованию является композиция. В вашем случае это будет означать применение шаблона Стратегия.
При применении этого шаблона ваш WorkWrapper станет контекст, а IWorker будет соответствовать роли абстрактная стратегия, а его реализации будут конкретные стратегии.
В коде:
class WorkWrapperContext {
private IWorker strategy;
WorkWrapperContext(IWorker strategy) {
this.strategy = strategy;
}
public void doBetterWorkOperation() {
// do some stuff
strategy.doWork();
}
}