这里的装饰者模式是否合适

时间:2016-05-09 15:02:42

标签: java decorator

所以我有几个课程,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)并且因为基类setter将返回一个实例键入BaseABCD,它不允许完全链接所有属性。

以下是我试图研究的一些可能的事情:

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

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

  3. 这些事情有可能吗?或者是否有更好的想法来解决像这样的问题?

    更多详情

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

    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中的键。在序列化时,我只是迭代该类的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应该是要走的路。它很简单,不易出错。但出于好奇,我想知道装饰模式是否可以在这里使用。毕竟,我在这里有一套完整的独立模块(属性)和不同的组合方式(类)。

3 个答案:

答案 0 :(得分:2)

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

为了帮助保持清洁,您可以添加工厂或构建器以简化操作。

以下是一些例子。假设基类被称为Base,子类是一个名为classes的字母,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();

  4. 这涉及为每个子类创建一个构建器类。子类构建器类可能会也可能不会从定义带有签名BaseBuilder的方法的抽象public Base create();类继承。

答案 1 :(得分:1)

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

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

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

另外,如果你很懒惰并且不关心实时性能,你可以使用反射来简化主类的编码。

答案 2 :(得分:0)

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

实际上并没有那么多属性,你可以将它们组合成随机排列。那么为什么要让每件事都变得更加复杂呢?

我认为装饰师不适合这里:装饰者的感觉是保持透明。如果您定义这样的属性,则必须检查(在属性的类型或存在上)每次访问。