Java шаблон проектирования для двух классов, использующих идентичные и похожие, но разные методы - PullRequest
3 голосов
/ 25 января 2020

Допустим, в моем приложении есть несколько служб, реализованных как ClassA и ClassB. Оба имеют некоторые сходства, но также и различия.

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

Мне трудно придумать "шаблон проектирования", как я мог бы организовать это более общим c способом. Начиная с 3. Я мог легко переместить этот метод в суперкласс, который расширяет ClassA и ClassB. Но как бы я мог разработать приложение так, чтобы 1. и 2. также учитывались?

Пункт 1. звучит немного как интерфейс для меня, но я понятия не имею, как это может быть объединен с суперклассом для пункта 3. А как насчет пункта 2?

Ответы [ 3 ]

4 голосов
/ 25 января 2020

Я бы разработал это так, чтобы class A и class B расширяли обобщенный класс c с параметром типа для типа параметра метода 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+ вы можете вместо этого использовать обобщенный c public interface Base<T> вместо абстрактного класса, предоставив log реализацию по умолчанию. Однако это не позволяет вам сделать log доступным только для реализующих классов и не препятствует переопределению подклассами log.

2 голосов
/ 26 января 2020

Как насчет использования композиции поверх наследования? Реализации start и process могут быть предоставлены функциями, как в примере ниже:

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

То же, что и в предыдущем примере, но с реализациями, предоставленными в конструкторе и использующими функциональные интерфейсы вместо лямбда-выражений.

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 голосов
/ 25 января 2020

Пусть другой класс реализует ведение журнала, обработка может выполняться классами, реализующими интерфейс службы:

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
  }

}
...