【问题标题】:How to implement repeated behavior when inheritance isn't an option当不能选择继承时如何实现重复行为
【发布时间】:2015-06-21 00:07:49
【问题描述】:

我有两个类共享一些属性和行为。一个这样的常见行为集群是 3D 空间中的操作。因此,每个都实现了接口Transformable

public interface Transformable {

    public void position (double x, double y, double z);
    public void position (Tuple3d tuple);

    public void rotateDeg (double yaw, double pitch, double roll);
    public void rotateRad (double yaw, double pitch, double roll);

    // And so on...

}

public class Foo extends Apple  implements Transformable { // Foo happens }
public class Bar extends Orange implements Transformable { // Bar happens }

现在,Transformable 中每个方法的行为对于任何实现类都是相同的,并且所需的代码很多。

FooBar 都扩展了我无法控制的不同超类,而且 Java 没有多重继承,所以这个选项不可用。

将大量代码复制粘贴到实现Transformable每个类中与所有编程事物都是对立的。

我能想到的最佳解决方案是创建一个包含所有实现细节的类,并使用传递方法:

public Foo extends Apple implements Transformable {

    // This class has all of the repeated implementation code
    private TransformationHelper helper;

    public final void rotate (double yaw, double pitch, double roll) {
        helper.rotate(yaw, pitch, roll);
    }
}

但是,这仅比在每个类中重复代码好一点。虽然实际的实现代码在一个地方,但这仍然很笨拙。

有没有人有更好的方法来解决这个问题?

编辑:为了澄清,我确实可以控制Transformable。整个想法是:FooBar 完全不同。它可以很容易地成为ShoeSpaceShuttle,但它们都存在于3D 空间中的不同位置,并且需要一个.position(x,y,z) 方法来完成完全相同的事情。

【问题讨论】:

  • 您当前的解决方案是使用具有委托设计模式的组合——这有什么问题?从我的 POV 来看,它看起来不错。
  • 如果行为完全相同,则将所需的getter和setter添加到接口并在接口内使用default methods。这样,如果需要,您可以偏离默认实现。
  • @HovercraftFullOfEels 似乎有更好的解决方案。我刚刚在过去一个小时内了解了设计模式......所以这是一个很好的候选者吗?
  • 即使 Foo 和 Bar 还没有扩展类,许多人仍然会选择组合和委托方法,因为它具有灵活性。
  • @drmuelr 你可以在你的接口中创建默认方法(仅对 java 8 有效),然后你所有实现这个接口的类将共享相同的定义方法,见Default Methods

标签: java oop interface


【解决方案1】:

另一个选项,对于您的情况可能可行,也可能不可行:考虑Iterator/Iterable 关系。类可以继承 Iterator 实现,而无需扩展继承代码的类。

在您的上下文中,Transformable 将有一种方法:

interface Transformable {
   Transformer getTransformer();
}

需要转换foo 引用的Foo 的调用者将使用foo.getTransformer() 获取Transformer,然后调用其方法进行转换。

Transformer 实现将扩展一个包含大部分代码的抽象类,但具有用于获取和设置周围对象的实际数字的抽象方法。

【讨论】:

    【解决方案2】:

    您的解决方案完全适用于 Java。您实际上是在使用设计模式Use Composition Over Inheritance

    正如@Naruto_Biju_Mode 在评论中所说,如果您使用的是 Java 8,则可以将实现移动到默认方法 Transformable 但是您必须使帮助程序类具有静态方法,因为您不能有任何接口中的实例字段

     interface Transformable {
    
    
    
        default void rotate (double yaw, double pitch, double roll) {
            TransformationHelper.rotate(yaw, pitch, roll);
        }
    
         ...
       }
    

    或者,您可以将完整的实现放在默认方法中,而不是辅助类。这取决于您是否需要在其他地方使用这些方法

    interface Transformable {
    
    
    
        default void rotate (double yaw, double pitch, double roll) {
           //actual implementation without helper class
        }
    
         ...
       }
    

    无论哪种方式,如果您使用默认方法,则只有在您想使用不同的实现时才需要覆盖

    【讨论】:

      【解决方案3】:

      (仅供参考,考虑删除我的帖子,因为标题说“继承不是选项”)

      你基本上首先创建一个抽象超类,比如说AbstractFruit,你仍然可以选择实现Transformable

      public abstract class AbstractFruit implements Transformable {
        @Override
        public void position (double x, double y, double z) {
          // provide implementation
        } 
        @Override
        public final void rotate (double yaw, double pitch, double roll) {
          // provide implementation
        }    
      }
      

      然后你可以让你的水果扩展这个抽象超类。

      【讨论】:

      • 我不能从任何公共类继承。以苹果和橙子为例。正如我所说,我的每个类都继承自我无法修改的不同超类。
      • 所以你只能控制FooBar,但不能控制AppleOrange,甚至Transformable?如果是这样的话——我最初从你的问题中并不清楚。另一方面,在这种情况下,我喜欢您提供的解决方案。您也可以考虑注射。
      【解决方案4】:
      public interface Transformable {
      
      public void position (double x, double y, double z);
      public void position (Tuple3d tuple);
      
      public void rotateDeg (double yaw, double pitch, double roll);
      public void rotateRad (double yaw, double pitch, double roll);
      
      // And so on...
      

      }

      public class ConcreteTransformable implements Transformable {
      
      //implement a concrete class here, you can even put your helper methods in here
      
       public void position (double x, double y, double z) {
      
       } 
      
       public void position (Tuple3d tuple) {
      
       }
      
       public void rotateDeg (double yaw, double pitch, double roll) {
      
       };
      
       public void rotateRad (double yaw, double pitch, double roll) {
      
       };
      

      }

      public Foo extends Apple implements Transformable {
      
      private Transformable transform = new ConcreteTransformable();
      
       //if your orange needs another transform than the standard, just pass the transformable via constructor
       public Apple(Transformable transform) {
           this.transform = transform;
       }
      
       public void position (double x, double y, double z) {
            transform.position(x,y,z);
       } 
      
       public void position (Tuple3d tuple) {
            transform.position(tuple);
       }
      
       //other methods also delegate to the transformable
      

      }

      【讨论】:

        猜你喜欢
        • 2010-11-15
        • 2020-08-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-05-27
        • 2011-11-21
        • 2019-01-12
        • 2010-09-27
        相关资源
        最近更新 更多