【问题标题】:Complementary generic types互补的泛型
【发布时间】:2013-01-16 11:24:31
【问题描述】:

前提:在我的项目中,我有两个通用类型的接口,分别定义了请求和响应。处理请求以产生响应,因此每个响应都是基于请求构建的。处理器接口处理请求以构建相应的响应。

代码:请求和响应接口为:

interface Request<T1>

interface Response<T2>

其中T2T1 分别代表通用请求和响应类型(为了清楚起见,我特意用不同的名称称呼它们)。

现在,由于T2 是一个请求,而T1 是一个响应,所以上面的代码演变为:

interface Request<T1 extends Response>

interface Response<T2 extends Request>

请注意:Request 和 Response 接口不共享任何继承关系 - 上面的代码只打算通信的是:Request 仅使用其他类型,即 Response。 p>

现在,考虑 Request 接口:由于 Response 再次被键入,并且由请求构建的响应将绑定到原始请求类型,因此,上述代码演变为:

interface Request<T1 extends Response<? extends Request<T1>>>

interface Response<T2 extends Request<? extends Response<T2>>

现在,处理器接口定义为:

interface Processor<R1 extends Request<R2>, R2 extends Response<R1>> {
    R2 process(R1 request);
}

具体类:

请求实现:

class ConcreteRequest implements Request<ConcreteResponse> {
    ConcreteResponse response;
    ...`
}

响应实现:

class ConcreteResponse implements Response<ConcreteRequest> {
    ConcreteRequest request;
    ...
}

处理器实现:

class ConcreteProcessor implements Processor<ConcreteRequest, ConcreteResponse> {
    ConcreteResponse process(ConcreteRequest request) {
    ...
    }
}

问题:上面的代码是否过度设计?是否有一种简化的方法来表示互补输入输出对象的元组?

【问题讨论】:

  • 抱歉,您能否再解释一下...Request&lt;T1 extends Response&gt; 为什么T1Request 会扩展Response 类型?
  • @Vijay:T1 不是响应,它是通用类型,表示如果处理它最终将生成响应...例如 List 表示包含 String 对象作为其元素……这就是你要问的吗?
  • RequestResponse 中使用泛型有什么好处?一个简单而具体的例子在这里会大有帮助。
  • @David: ...我的问题是:上面的代码是否过度设计? :) 我想我看到了你的答案......

标签: java oop generics typing


【解决方案1】:

在一个用例中,有一个通用的请求/响应(或者至少是请求)很有用。如果将请求生成为包含响应类型,则以下调用是“类型安全的”

public <messy define of T> T sendRequest(Request<T> request)  

现在该方法的用户将看到“类型安全”的请求-响应调用。我只称其为“类型安全”,因为该方法的实现可能必须将响应强制转换为 T,因此 ClassCastExceptions 理论上是可能的,但在大多数情况下,它们会被视为应用程序逻辑错误。

我不会将请求/响应的实际字段放在其他字段中,只需将通用类型信息用于“类型安全”的请求-响应调用。

【讨论】:

    【解决方案2】:

    我认为您不需要在类型定义中链接RequestResponse。它们由Processor 绑定。是不是有点像

    interface Requestable {
        ...
    }
    
    class Request<T extends Requestable> {
        ...
    }
    
    class Response<T extends Requestable> {
        ...
    }
    
    class Processor<T extends Requestable> {
        Response<T> process(Request<T> request) {
            ...
        }
    }
    

    够了吗?实际上,我不确定您是否需要泛型。

    【讨论】:

    • 感谢您的回复。在您的代码 sn-p 的上下文中,请求和响应不需要是通用类型的。
    【解决方案3】:

    除非我完全误解了你的问题,否则你不——也不应该——对这类问题使用泛型。使用多态性和/或组合将更合适。例如,如果您需要在响应中集成请求的副本(几乎没有必要但可以考虑),那么您可以在响应类中添加对请求对象的引用。

    从技术上讲,对Request 对象的引用可以使用类型来定义;但是,您不应该这样做,因为它始终是 Request 对象(基类或派生子类),而不是可能随响应的每次实例化而改变的某种任意类。

    当每个引用对象的类型完全不同时(例如,List &lt;String&gt;List&lt;Request&gt;StringRequest 对象之间没有子类关系)或当使用多态是不够的,因为您在子类中定义了一个或多个新的虚函数,而这些虚函数在超类中不存在。

    基于Request 构建Response 因为Request 被处理以产生Response 绝对不是要走的路,而您当前的Processor 界面就是证明。

    【讨论】:

    • 感谢您的回答。我理解你关于泛型为什么不适用的观点:泛型类型解决的可能类型之间“没有子类关系”,但为什么你认为“基于请求构建响应......绝对不是去……处理器接口就是证明。” - 它会导致任何反模式吗?如果你能解释一下。谢谢!
    • 在请求之后会构建响应并返回的事实并不意味着这两者之间存在分层的超类/子类关系。从 OOP 的角度来看,Response 和 Request 是属于两个不同类的两个不同对象。他们可能会共享一些通用功能、接口或其他元素(这一切都取决于您的代码),但您不应将这些共享元素与功能上的事实混淆,回复将作为对请求的回答被发回。
    • 为什么你认为 Request 和 Response 共享继承关系? (这是否从我的代码 sn-p 中得到暗示?如果是,我正在更新一个注释)事实上,我使用组合来维护一个在另一个中的引用。例如,唯一的继承是使用 Request 定义的泛型类型;这意味着请求通常与另一种类型相关联,即某种响应。
    • 此评论正是关于您愿意指定的这种通用关系(“interface Request” ...);没有具体说明您将如何在模板中实现它。关键是:为什么需要在通用模板中定义这样的功能关系?通用模板尚未以这种用途的语言实现。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-15
    • 2023-02-10
    • 2021-11-11
    相关资源
    最近更新 更多