两个类共享相同和相似但不同的方法的Java设计模式

6
假设我的应用程序有一些服务实现为ClassAClassB。两者都有一些相似之处,但也存在差异。
  1. 这两个类都有一个start()方法,具有相同的方法签名,但实现不同。
  2. 这两个类都有一个process()方法,具有不同的签名和不同的实现。
  3. 这两个类都有一个相同的log()方法,即代码完全相同。

Class 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;
    }
}

我在思考如何使用“设计模式”更加通用地组织这个内容,但遇到了些困难。至于第三点,我可以将这个方法轻松地移到一个超类中,让ClassAClassB来继承它。但是我该如何设计应用程序,以便同时考虑第一点和第二点呢?

第一点听起来有点像接口,但我不知道怎样与第三点的超类结合起来。那么第二点呢?


1
当然,你只能将共同的行为提取到共同的超类中。将不同的内容提取到共同的类中是没有意义的,对吧?因此,当你的方法具有不同的签名和不同的实现时,它们不应该放在任何共同的类中。简而言之:提取两个类共同的部分,并将差异留在具体的实现中。 - MakePeaceGreatAgain
2
也许可以使用泛型来处理具有不同参数的类似方法。但这高度取决于这些方法实际上要做什么,所以这只是一个猜测。我怀疑这里没有人能给你更具体的答案,因为你的问题太含糊了。 - MakePeaceGreatAgain
实现适配器。 - Florian Salihovic
值得问一下:你有没有考虑过将它们制作成同一个类(例如重命名一个“start”)?这些真的是必须放在不同类中的不同类型吗? - ernest_k
您没有提供足够的信息。不知道这些类的预期使用方式,就无法进行设计。当调用者调用例如 start() 时,它是否希望在 A 和 B 中具有统一的语义?其他方法呢? - root
启动、处理和日志方法之间有什么关系?客户端将如何使用这些类?它们是否预计在类似的用例中使用?完全有可能两个类恰好具有相似的接口,但实际上并不相关。是否存在客户端希望将A和B视为相同的情况? - plalx
3个回答

5
我会设计这样一个方案: 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<T>代替抽象类,通过给log提供默认实现。然而,这并不能让log只对实现类可见,并且不能防止子类覆盖log

1
这解决了OP的问题,但继承是脆弱的。如果未来的ClassC需要适应它,你会看到它是否能够保持。很难预测未来,这就是继承所暗示的。 - Fuhrmanator

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
    }
}

0

让不同的类实现日志记录,处理可以由实现服务接口的类完成:

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
  }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接