【问题标题】:How can Kotlin extension classes replace the decorator design pattern?Kotlin 扩展类如何替代装饰器设计模式?
【发布时间】:2021-07-01 00:22:36
【问题描述】:

Kotlin 的文档做出了这样的声明:

Kotlin 提供了使用新功能扩展类的能力 无需从类继承或使用设计模式,例如 作为装饰者。

Kotlin Extensions

我无法理解扩展函数如何完全替代装饰器设计模式。

借用 TutorialPoint 的一个例子,你如何把下面的例子变成只使用扩展函数的代码?您能否在不牺牲对具体形状对象和装饰形状对象调用 draw() 的能力的情况下做到这一点?

interface Shape {
 fun draw();
}

class Rectangle: Shape {
 override fun draw() {
  println("Shape: Rectangle")
 }
}

class Circle: Shape {
 override fun draw() {
  System.out.println("Shape: Circle");
 }
}

abstract class ShapeDecorator(protected val decoratedShape: Shape): Shape {

 override fun draw(){
  decoratedShape.draw();
 }
}

class RedShapeDecorator(decoratedShape:Shape): ShapeDecorator(decoratedShape) {

 override fun draw() {
  decoratedShape.draw();
  setRedBorder(decoratedShape);
 }

 private fun setRedBorder(decoratedShape:Shape){
  System.out.println("Border Color: Red");
 }
}

fun main(){

 val circle: Shape = Circle();

 val redCircle: Shape  = RedShapeDecorator(Circle());

 val redRectangle: Shape = RedShapeDecorator(Rectangle());
  
 
 System.out.println("Circle with normal border");
 circle.draw();

 System.out.println("\nCircle of red border");
 redCircle.draw();

 System.out.println("\nRectangle of red border");
 redRectangle.draw();
}

TutorialPoint Example in Java

【问题讨论】:

  • 扩展并不能替代所有可能使用的装饰器模式。您引用的声明只是说它可以如果您仅使用该模式将函数添加到类而不对其进行子类化,则可以避免对装饰器模式的需要。
  • 好的,如果具体装饰器向具体装饰类中的方法添加额外代码,那么扩展不能替换装饰器设计模式?在 RedShapeDecorator 中的示例 draw() 中打印“边框颜色:红色”以及形状名称。这不能用扩展重写吗?
  • 这个例子与扩展函数的作用不同,因为它们不能影响继承或覆盖函数。
  • Kotlin 文档中的这个声明说扩展只是扩展现有类的另一种方式。扩展与装饰器模式或子类型有很大不同,因为用户需要有意使用它们,而子类型/装饰器对用户是透明的。这就是为什么扩展不能真正替代其他模式的原因。它们只是另一种方便的工具,比装饰器更轻巧,但功能有限。

标签: kotlin design-patterns decorator


【解决方案1】:

这似乎是一个设计推理或哲学的问题。第一个装饰器模式更广为人知的是包装类。包装器/装饰器这样做是为了向功能(正在被包装的)/wrappee 的用户隐藏细节,添加或更改被包装的功能。然后它添加了一层抽象。有了这个抽象,如果需要,它可以在将来轻松更改 wrappee 类。 在您的示例中,我看不到 ShapeDecorator/ShapeWrapper 的目的-它真正的包装是什么,目的是什么? - 接口本身就是一个合约。

默认情况下,在 Kotlin 中,所有类和函数都是最终的和关闭的。这样做是为了强制执行一些有效的 Java 原则 (Joshua Bloch)。因此再次强制您不要继承类,而是使用 Wrapper Pattern 或 Extension。

现在来看看 Extension 和 Wrapper 之间的区别。如果您只需要扩展以添加基类的原始创建者错过的一些功能,并且您无法在基类中进行更改或觉得它不是这样做的正确位置,那么您将使用扩展功能。它没有隐藏任何东西,没有添加任何抽象层(一个或多个)。 但是,如果是出于抽象、装饰的目的,那么你就去装饰器吧。 扩展函数不会取代装饰器模式。如果不需要,可以避免。

静态分派的扩展也很有趣 - 用非常简单的话来说,它们成为 java 反编译中的静态方法(因此你再次看到抽象缺失了)。

这是一个快速编写的答案。

【讨论】:

    猜你喜欢
    • 2018-09-21
    • 2013-02-19
    • 2010-10-05
    • 1970-01-01
    • 1970-01-01
    • 2021-07-07
    • 2013-11-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多