【问题标题】:Find a Decorator of a particular type when using the Decorator Pattern?使用装饰器模式时找到特定类型的装饰器?
【发布时间】:2018-04-29 14:01:47
【问题描述】:

我正在构建一个需要使用多种类型的类似传感器的应用。由于传感器也可能有不同的行为或行为组合,我决定使用decorator pattern

简而言之,我的层次结构如下所示:

所以任何具体的ISensorDecorator 类都可以装饰(包装)任何具体的IMeasureSensor 类,但是由于具体的ISensorDecorator 也是具体的IMeasureSensor,它们可以相互包装。比如

IMeasureSensor sensor = new FilteredSensorDecorator(
    new CalibratedSensorDecorator(
        new AccelerometerSensor()
    )
);

是声明经过过滤和校准的加速度计传感器的有效语句。

现在假设我在CalibratedSensorDecorator 中有一个名为setCalibration() 的方法。显然我不能打电话

sensor.setCalibration();

因为IMeasureSensor 没有setCalibration() 方法。并尝试使用

((CalibratedSensorDecorator)sensor).setCalibration()

也不行,因为sensorFilteredSensorDecorator

在这种特殊情况下,我如何才能访问CalibratedSensorDecorator,更一般地说,如何访问任何装饰器“链”中的任何特定装饰器?我不想将它们存储为单独的变量,我想动态地进行。

【问题讨论】:

    标签: java android design-patterns reflection decorator


    【解决方案1】:

    因为这是一个设计问题,所以不会有任何正确答案,您需要做出选择,哪个好,也可以不那么好。

    您不应该为特定类添加方法,因为它会违反 Liskov substitution principle

    程序中的对象应该可以被其子类型的实例替换,而不会改变该程序的正确性。

    您可以在构造函数CalibratedSensorDecorator 中初始化calibration,并在执行所需函数时使用它。

    如果这不符合您的要求,那么CalibratedSensorDecorator 可能不属于您的传感器层次结构。考虑将其分离并使用策略模式来决定使用哪一个。

    编辑 1:

    我的理解并不是说您不应该向子类型添加方法?

    是的,你是对的。它不禁止添加方法,但如果方法正在更改对象的状态,则应重新考虑。所有这些模式只是指导方针,可以根据我们的需要进行调整。

    解释我的理由:

    假设您在CalibratedSensorDecorator 上创建了setCalibration()。您可以通过以下方式将CalibratedSensorDecorator 公开给内部开发人员或外部开发人员。您创建了一个仅返回 IMeasureSensor 的工厂,如下所示:

    public IMeasureSensor getCalibratedSensor(){
        ...
    }
    

    现在,您的 API 用户只需了解这一点,并且很高兴他/她当前的代码可以正常工作。但意识到他/她错过了经过数小时调试后发现的setCalibration()。此外,他/她必须编写类型检查和类型转换代码才能使用此功能,这对于干净的代码可能不是很好。

    您应该尽量保持您的类不可变,以便调试和维护方便。重新创建对象没有害处,因为旧的对象将被垃圾回收。

    这只是我的建议,您决定仔细考虑什么最适合您的用例。如果它是强制性的,您仍然可以继续使用您的新方法来创建该方法,并确保已制作适当的文档以使用户了解其用法。

    【讨论】:

    • 我不是 Liskov 替换原则方面的专家,但据我了解,这并不是说您不应该向子类型添加方法?如果是这样的话,继承的用处不是很有限吗?我实际上正在按照您的建议做,在构造函数中采用calibration 参数。但是我希望能够更改校准和其他参数,而不必仅仅为了更改变量而重新创建新对象...
    • 谢谢,我明白你的意思了。如果我改用策略模式,我将如何将任意数量的策略“链接”在一起?
    • 那么策略可能是将您的传感器分为两种:一种不需要calibration,另一种需要校准。假设IMeasureSensor 是一个没有calibrationIMeasureSensorWithCalibration 的另一个,让您的API 的用户决定他/她更喜欢哪一个。您还可以在IMeasureSensorWithCalibration 中扩展IMeasureSensor ,以便指示接口隔离。
    【解决方案2】:

    虽然 Sagar 的回答讨论了考虑使用装饰器模式以外的其他方法的一些(有效)原因,但我为寻找正确装饰器的实际问题提出了一个可行的解决方案。

    /**
     * Walks the decorator hierarchy recursively from the outside in (excluding the final
     * IMeasureSensor which is not a ISensorDecorator), and returns the decorator of the given class.
     * If none can be found, null is returned.
     */
    IMeasureSensor findDecorator(IMeasureSensor sensor, Class decoratorClass){
        if( ISensorDecorator.class.isAssignableFrom(sensor.getClass()) ){
            return (sensor.getClass() == decoratorClass) 
                ? sensor 
                : findDecorator(((ISensorDecorator) sensor).getDecoratee(), decoratorClass);
        } 
        else
            return null;
    }
    

    ISensorDecorator.getDecoratee() 方法只返回“装饰者”,即装饰器装饰的 IMeasureSensor

    public IMeasureSensor getDecoratee(){
        return mMeasureSensor;
    }
    

    然后您可以使用findDecorator() 来查找给定类型的(最外层)装饰器,如下所示:

    IMeasureSensor sensor;
    
    ...
    
    CalibratedSensorDecorator s = (CalibratedSensorDecorator) findDecorator(sensor, CalibratedSensorDecorator.class);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-05-07
      • 2012-01-28
      • 1970-01-01
      • 2012-02-29
      • 2022-08-15
      • 1970-01-01
      • 2020-02-29
      • 1970-01-01
      相关资源
      最近更新 更多