使用没有方法的接口

时间:2012-04-20 07:29:39

标签: oop marker-interfaces

我已经将实际代码简化为说明这一点的最小例子。请原谅缺乏制定者/吸气者等。

想象一下,我们有几个客户按顺序浏览的网页。这里的用例是: -

  • 用户选择他们想要的书
  • 用户选择是否希望通过邮寄或电子邮件及相关详细信息发送
  • 系统履行订单

这个问题集中在两种交付方式上。建模如下:

interface DeliveryDetails
{
    // Implementations of this have nothing in common other than that they
    // fulfil the same logical role.
}

class EmailDeliveryDetails implements DeliveryDetails
{
    String emailAddress;  // It really has a constructor and getter, I promise.
}

class PostalDeliveryDetails implements DeliveryDetails
{
    String streetAddress;
    String Country;
}

现在,为了表示用户在浏览页面时输入的信息,我们有这个类:

class PurchaseData
{
    String title;
    DeliveryDetails deliveryDetails;
}

当用户浏览网页时,信息存储在PurchaseData的实例中。如果用户返回页面,我们可以向他们显示他们之前输入的内容。在用户确认并且是时候发送图书后,deliveryDetails会引用PostalDeliveryDetailsEmailDeliveryDetails的实例。

总之,当用户确认他们的信息时:

    // Some code in a factory
    if ( purchaseData.deliveryDetails instanceof EmailDelivery )
    {
        // construct a EmailDeliveryService( purchaseData, SMTP details, etc ... )
    }
    if ( purchaseData.deliveryDetails instanceof PostalDelivery )
    {
        // construct a PostalDeliveryService( purchaseData, etc ... )
    }
}

Delivery接口没有方法让我很烦恼。

这是由电子邮件和邮政投递之间的差异所迫。

我认为DeliveryDetails.deliver()不是一个好方法,因为这会强制实现静态地获取SMTP服务器地址。这使问题(管道与用户输入的信息)混淆。

如果你必须存储一些任意类型的东西,泛型可能很有用。无法使用泛型(PurchaseData<T extends Delivery>),因为在创建PurchaseData实例时不知道传递类型。无论如何,这对工厂没有帮助。

这个空接口好吗?有没有更好的方法来设计此代码?

4 个答案:

答案 0 :(得分:1)

对我来说,EmailDeliveryDetailsPostalDeliveryDetails之间的(数据)差异归结为地址。所以我的第一直觉就是将这些数据提取到一个单独的Address类中。然后,您可以决定使用带有emailAddress和streetAddress的可选字段的单个地址,或者具有电子邮件和邮政地址的不同子类的类层次结构。

我更喜欢带有可选字段的单一类,因为它使用起来更干净,而且对我来说实用性胜过概念“纯度”。

更新

基于以下评论链:

当特定类的属性(因此可能的状态)之间没有重叠时,尝试以多态方式处理它们是非常尴尬的。如果一个人不打算在其中加入很多功能,那么在继承一些通用接口的不同类中处理它们就更加困难了(正如你所指出的那样)。 OTOH在概念上所有这些都是某种地址数据,所以它可以在一个类中处理。

请注意,大部分内容都是猜测 - 如果没有更详细的信息,很难对您的设计进行推理。

  

你是对的,我不打算在EmailDeliveryDetailsPostalDeliveryDetails中放置太多行为。在实际应用程序中,这些详细信息将保留在数据库中,并将详细信息发送到外部系统。

好的,所以你实际上并没有多态地对待它们。您只需要一个通用的“句柄”来访问要保留的不同数据位。并且持久性通常不关心多态性和接口。在这种情况下,您的类继承自空接口是可以的。

答案 1 :(得分:1)

你的两个实现类都是某种描述符,它们实际上并没有做任何事情。对我来说,在这种情况下使用类层次结构感觉更清晰(带有两个子类的抽象基类:EmailDeliveryDetails和PostalDeliveryDetails),即使基类是空的。 EmailDeliveryDetails DeliveryDetail而不是Delivery实现,除非它实现了交付方法。

答案 2 :(得分:1)

如下所述的双重调度方法可以起作用,但在这种情况下可能过度。我会提到它只是为了给你一些选择......

class DeliveryService {
    // base class doesn't handle anything
    process(EmailDelivery details) {}
    process(PostalDelivery details) {}    
}

class EmailDeliveryService extends DeliveryService {
    process(EmailDelivery details) { /* handle */ }
}

class PostalDeliveryService extends DeliveryService {
    process(PostalDelivery details) { /* handle */ }
}

interface DeliveryDetails {
    processWith(DeliveryService service);
}

class EmailDeliveryDetails implements DeliveryDetails {
    processWith(DeliveryService service) { service.process(this); }
}

// try all services (of unknown type) on the given details (also of unknown type)
List<DeliveryService> services = configureServices();
DeliveryDetails details = getDetails();
for (DeliverySerivce service : services) details.processWith(service);

如果添加了新类型的DeliveryDetails(可能不太可能......),则必须更新DeliveryService(添加空进程方法)并添加一种新类型的DeliveryService,它实际上对新类型的DeliveryDetails执行某些操作。

可选地,进程和processWith方法可以返回布尔值以指示是否处理了细节。

同样,在这种情况下,这种方法可能不必要地复杂,但它解决了处理最后提出的未知交付类型的问题。

答案 3 :(得分:1)

EmailDeliveryDetailsPostalDeliveryDetails只有“交付”的概念,但他们的行为和知识都不相同。在这种情况下,共同的祖先DeliveryDetails不适合。

就个人而言,我会从PurchaseData创建一个继承树。