【问题标题】:Java design pattern for two classes sharing identical and similar but different methods两个类共享相同和相似但不同的方法的 Java 设计模式
【发布时间】:2020-01-27 23:06:57
【问题描述】:

假设我的应用程序有一些服务实现为ClassAClassB。两者有一些相似之处,但也有不同。

  1. 两个类都有一个start() 方法,具有相同的方法签名但不同的实现。
  2. 两个类都有一个 process() 方法,具有不同的签名和不同的实现。
  3. 两个类都有相同的log() 方法,即代码完全相同。

A 类

public class ClassA {

    public String start(String s1, String s2) {
        startImplementation();
        return someString;
    }

    public String process(String s) {
        processingImplementation();
        return processedString;
    }

    private String log(String s) {
        logImplementation();
        return sharedString;
    }
}

B 类

public class ClassB {

    public String start(String s1, String s2) {
        otherStartImplementation();
        return someString;
    }

    public String process(Long l) {
        otherProcessingImplementation();
        return processedString;
    }


    private String log(String s) {
        logImplementation();
        return sharedString;
    }
}

我无法思考如何以更通用的方式组织它的“设计模式”。从 3 开始。我可以轻松地将这个方法移动到 ClassAClassB 扩展的超类。但是我将/可以如何设计应用程序以便同时考虑 1. 和 2.?

第 1 项对我来说听起来有点像一个接口,但我不知道如何将它与第 3 项的超类结合起来。那么第 2 项呢?

【问题讨论】:

  • 当然你只能将common行为提取到common超类。将 不同 的东西提取到 common 类中是没有意义的,不是吗?所以当你的方法有不同的签名和不同的实现时,它们不应该去任何公共类。简而言之:提取两个类的共同点,并在具体实现中留下差异。
  • 也许 oan 对具有不同参数的类似方法使用泛型。但这在很大程度上取决于这些方法的实际作用,所以这只是一个猜测。我怀疑这里的任何人都可以给你一个更具体的答案,因为你的问题很模糊。
  • 实现适配器。
  • 值得一问:你有没有想过让他们成为同一个班级(比如一个start重命名)?这些真的是不同的类型,必须属于不同的类吗?
  • 您没有提供足够的信息。如果不知道如何使用这些类,就无法设计它。当来电者打电话时,例如start(),它是否期望 A 和 B 之间的语义一致?其余的方法呢?

标签: java oop design-patterns


【解决方案1】:

我会这样设计,以便 class Aclass B 扩展一个通用抽象类,并为 process 方法的参数类型提供一个类型参数。

public abstract class BaseClass<T> {
    public abstract String start(String s1, String s2);

    public abstract String process(T value);

    protected final String log(String s) {
        // shared log implementation
    }
}
public class A extends BaseClass<String> {
    @Override
    public String start(String s1, String s2) {
        // A.start implementation
    }

    @Override
    public String process(String s) {
        // A.process implementation
    }
}
public class B extends BaseClass<Long> {
    @Override
    public String start(String s1, String s2) {
        // B.start implementation
    }

    @Override
    public String process(Long l) {
        // B.process implementation
    }
}

在 Java 9+ 中,您可以改为使用通用的 public interface Base&lt;T&gt; 而不是抽象类,方法是为 log 提供默认实现。但是,这不允许您使 log 只能由实现类访问,并且不会阻止子类覆盖 log

【讨论】:

  • 这解决了 OP 的问题,但是继承很脆弱。如果它需要适应,你会看到它是否适合未来ClassC。很难预测未来,这就是继承所暗示的。
【解决方案2】:

如何使用组合而不是继承? startprocess 的实现可以由如下示例中的函数提供:

import java.util.function.BiFunction;
import java.util.function.Function;

class X<T> {
    public String start(BiFunction<String, String, String> f, String s1, String s2) {
        return f.apply(s1, s2);
    }

    public String process(Function<T, String> f, T t) {
        return f.apply(t);
    }

    // example
    public static void main(String[] args) {
        X<String> xString = new X();
        xString.start((s1, s2) -> s1 + s2, "a", "b");

        X<Long> xLong = new X();
        xLong.process((t) -> { Long tt = t * 2;return tt.toString(); }, 4L);
    }
}

与前面的示例相同,但在构造函数中提供了实现并使用函数式接口而不是 lambda。

import java.util.function.BiFunction;
import java.util.function.Function;

class StartFunctionExample implements BiFunction<String, String, String> {
    @Override
    public String apply(String s1, String s2) {
        return s1 + s2;
    }
}

class ProcessFunctionExample implements Function<Long, String> {
    @Override
    public String apply(Long t) {
        Long tt = (t * 2);
        return tt.toString();
    }
}

class Z<T> {
    private final BiFunction<String, String, String> startFunction;
    private final Function<T, String> processFunction;

    public Z(
            BiFunction<String, String, String> startFunction,
            Function<T, String> processFunction
    ) {
        this.startFunction = startFunction;
        this.processFunction = processFunction;
    }

    public String start(String s1, String s2) {
        return startFunction.apply(s1, s2);
    }

    public String process(T t) {
        return processFunction.apply(t);
    }

    // example
    public static void main(String[] args) {
        Z<Long> xLong = new Z(new StartFunctionExample(), new ProcessFunctionExample());
        xLong.start("a", "b"); // ab
        xLong.process(7L);     // 14
    }
}

【讨论】:

    【解决方案3】:

    让不同的类来实现日志,处理可以通过实现服务接口的类来完成:

    interface Processable {
      String start(String s1, String s2);
      process(String s);
    }
    
    class LogDecorator {
    
      private Processable p;
    
      public LogDecorator(Processable p) {
        this.p = p;
      }
    
      public String start(String s1, String s2) {
        p.start(s1, s2);
      }
    
      public String process(String s) {
        p.process();
      }
    
      protected final String log(String s) {
        // logging
      }
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-12-01
      • 2012-03-14
      • 2019-06-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-18
      相关资源
      最近更新 更多