【问题标题】:Is decorator pattern suitable here装饰器模式适合这里吗
【发布时间】:2016-09-04 06:51:09
【问题描述】:

所以我有几节课,ABC、...N。还有几个可能的属性,setP()setQ()setR()、...setZ()。现在,每个类都可以有不同的属性组合:

A          B          C          N
- setX     - setR     - setP     - setY
- setY     - setZ     - setQ     - setZ
- setZ                - setR
                      - setS
                      - setZ

所有的setter都返回对象本身的一个实例,这样就可以进行链接了。

我试图找到一种优雅的方法来解决这个问题。我不想重新定义每个类中的数十个属性(代码重复),我不想使用继承,因为会有丑陋、毫无意义的中间类(BaseABCD -> setZ)并且因为基类设置器将返回@类型的实例987654331@ 这将不允许所有属性的完整链接。

这里有一些我试图调查的可能的事情:

  1. 以某种方式以优雅的方式将每个属性定义为装饰器,并编写 A 类似 Base.decorators().addX().addY().addZ().finalize() 的东西。

  2. Base 中定义所有可能的属性,并在派生类中隐藏不需要的属性。我认为这是不可能的。

这些事情有可能吗?还是有更好的办法来解决这样的问题?


更多详情

这些类基本上是应用程序用来与外部系统通信的不同消息类型。不同的消息包含不同类型的不同字段。例如:

ECHO message:
- Timestamp (DateTime)
- SourceAddress (String)

CHECK RESOURCE message:
- Timestamp (DateTime)
- Resource Identifier (Integer)

MANIPULATE RESOURCE
- Resource Identifier (Integer)
- Operation Type (Enum)

消息在传输时被序列化为字符串,并且不保留类型信息。所以我选择了HashMap<String, String>。每个属性对应于该类的哈希图中的一个键。序列化时,我只是遍历该类的 hashmap 中的所有键/值条目并生成要发送的字符串消息表示。

但是,我想对调用者代码强制执行类型。所以我不想暴露像A.set('RESOURCEIDENTIFIER', '123456') 这样的方法。相反,我想公开像 A.setResourceIdentifier(123456)C.setOperation(OperationType.DELETE) 这样的方法。

在内部,这些 setter 函数只是将一个相关的键放入 hashmap 并为其分配一个字符串值。

public A setOperation(OperationType operation) {
    this.hashmap.put('OPERATION', operation.name());
    return this;
}

大约有 40 种独特的字段类型,所有消息都使用这些字段的独特子集。比如AB 包含setTimestamp()BC 包含 setResourceIdentifier()。只有C 包含setOperationType()。以此类推。

我不想一遍又一遍地重新定义每个类中的这几十个属性。这就是为什么我想探索以下两个选项:

选项 1 定义具有所有可能属性的Base 类,并在派生的A 类中,仅覆盖所需的公共属性。这是可行的。但我想看看是否有可能实现选项 #2 中描述的内容。

选项 2 以某种方式定义装饰器和工厂类,以便

public ? getA() {
    return Base.startDecorating()
        .addTimestamp()
        .addResourceIdentifier()
        .finalize();
}

? objA = Factory.getA()
    .setTimestamp(DateTime.now())
    .setResourceIdentifier(123456);

这可能吗?在写出这个问题时,我意识到选项 1 应该是要走的路。它简单且不易出错。但出于好奇,我想知道是否可以在这里使用装饰器模式。毕竟,我这里拥有的是一套完整的独立模块(属性)以及将它们组合在一起的不同方式(类)。

【问题讨论】:

  • 在这种情况下继承有什么难看的?你能提供更具体的例子吗? setX Y Z 没有帮助。
  • 创建这些对象的类或方法是否与基类或子类交互?以 List 为例,它是否知道它使用的是 LinkedList 而不是一个 List?
  • 用一个基础枚举集来保存属性的基类怎么样?
  • @Pacente。这当然是当前的实现,但我想看看我是否可以成功地将装饰器模式应用于这个问题。但看起来很难实现。

标签: java decorator


【解决方案1】:

如果属性的设置器中有一些在多个主类之间共享的逻辑,那么装饰器模式可以适合并且是一种很好的设计方法。如果没有,例如你确定他们所做的只是this.x=x 或者每个类设置某种类型的属性不同然后没有。

您可以为每个属性定义一个类,并让您的主类具有属性类类型的变量。这样,当在主类上设置属性时,它会将作业委托给相应属性类的设置器,这样您只需定义一次属性的设置器并在任何地方使用它们。同样,如果您的设置器中有更多逻辑然后this.x=x;,这可能是最好的主意。

哦,在将设置的作业委托给属性类之后,您仍然可以返回 this 进行链接。

此外,如果您比较懒惰并且不太关心实时性能,您可以使用反射来简化主类的编码。

【讨论】:

    【解决方案2】:

    装饰器是为你可以不断添加的东西而设计的。就像一棵圣诞树。但是一旦圣诞树被装饰了,你就可以使用像turnOn()这样的常用方法。它不是为向现有 API 添加新方法而设计的。我认为您可以为此使用常规的旧继承。我也会使用 List 之类的示例。您的 Base 类可以提供所有将共享的公共方法,然后其他子类将添加新方法。

    为了帮助保持清洁,您可以添加工厂或生成器以使事情变得更容易。

    这里有一些例子。假设基类名为Base,子类为一个字母命名的类,AB

    1. 工厂示例 1.

      Base x = factory.getInstance(A.class);

    2. 工厂示例 2.

      Map props = ...; Base x = factory.getInstance(A.class, props);

    3. 构建器示例 1。

      Base x = new ABuilder().setX(x).setY(y).setZ(z).create();

    这涉及为每个单独的子类创建一个构建器类。子类构建器类可能继承也可能不继承自抽象 BaseBuilder 类,该类定义了带有签名 public Base create(); 的方法。

    【讨论】:

    • 谢谢。这似乎是一个有趣的想法。我一定会试试这个,看看结果如何。 :)
    【解决方案3】:

    从属性的名称我猜你想定义不同的坐标表示?为什么不使用一个具有所有属性的基类?

    这些实际上并没有那么多属性,您不能将它们组合成随机排列。那么,为什么要让每件事都变得比必要的更复杂呢?

    我认为 Decorator 在这里不太合适:decorator 的意义在于保持透明。如果你这样定义你的属性,你必须检查(关于属性的类型或存在)每个访问。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-06-14
      • 1970-01-01
      • 2011-10-23
      • 1970-01-01
      • 2011-09-08
      • 1970-01-01
      • 2019-02-23
      • 2012-09-21
      相关资源
      最近更新 更多