【问题标题】:OOP design - when to have a common base class [Flaw design?]OOP 设计——什么时候有一个通用的基类 [缺陷设计?]
【发布时间】:2018-07-04 01:21:47
【问题描述】:

我有一个简化的设计如下(基本上是一堆处理程序来处理 2 种不同类型的请求:EventRequest 和 SpeechRequest)。下面是伪代码:

class SpeechRequest {sessionId: String; slot: String}
class EventRequest {sessionId: String; event: String}

class SpeechRequestHandler; 
class EventRequestHandler;

class SpeechRequestHandler[A/B/C] extends SpeechRequestHandler {
    - handle(request: SpeechRequest) {      
        doSt(request.slot)
    }
}

class EventRequestHandler[A/B/C] extends EventRequestHandler {
    - handle(request: EventRequest) {       
        doSt(request.event)     
    }   
}

有2个不同的dispatcher为每种类型的请求找到合适的handler,然后转发给handler去处理:

class SpeechDispatcher {
    - handle(request: SpeechRequest) {
        handler: SpeechRequestHandler = findHandlerToHandle(request);
        handler.handle(request);
    }
}

class EventDispatcher {
    - handle(request: EventRequest) {
        handler: EventRequestHandler = findHandlerToHandle(request);
        handler.handle(request);
    }
}

现在,我想重构并创建一个基类/通用类。当然,我想出了这个:

class Request {sessionId: String}
class SpeechRequest extends Request {slot: String}
class EventRequest extends Request {event: String}

class RequestHandler {
    - canHandleRequest(Request): bool
    - handle(Request)
}

class SpeechRequestHandler extends RequestHandler {
    - canHandleRequest(request: Request): bool = request instanceof SpeechRequest
}

class EventRequestHandler extends RequestHandler {
    - canHandleRequest(request: Request): bool = request instanceof EventRequest
}

class SpeechRequestHandler[A/B/C] extends SpeechRequestHandler {
    - handle(Request: request) {
        //need to cast to specific type to extract a certain fields to do some operation
        //!! I feel something is not right because of that
        speechRequest:SpeechRequest = (SpeechRequest)request;
        doSt(speechRequest.slot)

        //other operation can work with base Request object; so it's OK
    }
}

class EventRequestHandler[A/B/C] extends EventRequestHandler {
    - handle(Request: request) {
        eventRequest:EventRequest = (EventRequest)request;
        doSt(eventRequest.event)

        //other operation can work with base Request object; so it's OK
    }   
}

对于所有 SpeechRequestHandler[A/B/C]:handle 函数,我现在需要将 Request 对象转换为 (SpeechRequest) 对象:speechRequest:SpeechRequest = (SpeechRequest)request;

我觉得我的设计有缺陷。如果每个 SpeechRequestHandler 我都需要将对象转换为 (SpeechRequest) 以便我可以对这些信息做一些事情,这是否意味着在这种情况下重构基类没有意义?

请您建议一种更好的方法或设计模式来干净地处理这个问题。

谢谢。

【问题讨论】:

  • 您可能需要标记正确的语言。
  • 您能否使用在RequestHandler 上定义的泛型来限制实现中预期事件的类型?
  • @MadProgramer:不,我不想使用泛型,因为我的处理程序是由 spring bean 初始化的,并且它不能很好地与泛型一起使用。此外,它将增加额外的复杂性。我只是想知道将类转换为上述特定类型是否正常?

标签: java oop design-patterns class-design


【解决方案1】:

如果你想保持类型安全,你可以通过双重调度来做这种事情。见https://en.wikipedia.org/wiki/Double_dispatch

看起来你想要这样的东西:

interface HandlerSet{
    SpeechHandler getSpeechHandler(Request request);
    EventHandler getEventHandler(Request request);
}

abstract Request {
    ... common stuff ...
    abstract handleWith(HandlerSet handlers);
}
class SpeechRequest extends Request {
    sessionId: String; slot: String
    handleWith(HandlerSet handlers) {
        return handlers.getSpeechHandler(this).handle(this);
    }
}
class EventRequest extends Request {
    sessionId: String; event: String
    handleWith(HandlerSet handlers) {
        return handlers.getEventHandler(this).handle(this);
    }
}

【讨论】:

    【解决方案2】:

    主要的问题是你引入了一个可以被Request的每一种类型调用的方法,但是请求的具体处理程序必须知道具体的类型。

    类似的东西

     public void handleRequest(Request request){
          // Find the correct handler
          for(RequestHandler requestHandler : requestHandlers){
               if(requestHandler.canHandle(request)){
                   // Opps, the handler needs the concrete type
                   requestHandler.handle(request);
               }
          }
     }
    

    在运行时,唯一有足够信息知道哪个处理程序可以处理它的对象是Request。因此,您必须要求请求选择要调用的特定目标方法。

    这称为visitor pattern

    public interface Request {
        public void accept(RequestVisitor requestVisitor);
    }
    
    public interface RequestVisitor {
        public void visit(SpeechRequest speechRequest);
        public void visit(EventRequest eventRequest);
    }
    

    Request 实现现在可以决定必须调用 RequestVisitor 的哪个回调:

    public class SpeechRequest implements Request {
        public void accept(RequestVisitor requestVisitor) {
            requestVisitor.visit(this);
        }
    }
    
    public class EventRequest implements Request {
        public void accept(RequestVisitor requestVisitor) {
            requestVisitor.visit(this);
        }
    }
    

    然后具体的处理程序可以使用具体的请求对象

    public class SpeechRequestHandler {
        public void handle(SpeechRequest request) {
            System.out.println(request.getClass().getSimpleName());
        }
    }
    
    public class EventRequestHandler {
        public void handle(EventRequest request) {
            System.out.println(request.getClass().getSimpleName());
        }
    }
    

    从客户的角度来看,访问者可能会被这样使用:

    SpeechRequestHandler speechRequestHandler = new SpeechRequestHandler();
    EventRequestHandler eventRequestHandler = new EventRequestHandler();
    
    RequestVisitor requestVisitor = new RequestVisitor() {
    
        @Override
        public void visit(EventRequest eventRequest) {
            eventRequestHandler.handle(eventRequest);
        }
    
        @Override
        public void visit(SpeechRequest speechRequest) {
            speechRequestHandler.handle(speechRequest);
        }
    };
    
    Request request1 = new SpeechRequest();
    Request request2 = new EventRequest();
    
    request1.accept(requestVisitor);
    request2.accept(requestVisitor);
    

    但是使用访问者模式通常(但不总是)暗示您试图使界面过于抽象。这就是为什么您丢失了稍后必须重新创建的类型信息。也许你会找到更好的设计。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-08-19
      • 1970-01-01
      • 1970-01-01
      • 2011-10-11
      • 2013-10-03
      • 1970-01-01
      相关资源
      最近更新 更多