Как создать код вокруг непересекающихся типов, которые имеют похожее поведение? - PullRequest
0 голосов
/ 11 октября 2019

Предпосылка

Существующая кодовая база (вряд ли изменится):

public interface Shape {
  void print();
}

И с реализациями, такими как (Круг, Треугольник, Квадрат и т. Д.):

public final class Circle implements Shape {

  private final CircleInput input;

  public Circle(CircleInput input) {
    this.input = input;
  }

  @Override
  public void print() { ... }
}

CircleInput, TriangleInput и SquareInput НЕ связаны вообще (непересекающиеся типы).

Задача

Входные объекты могут быть очень сложными, и я хочу, чтобы пользователи создали класс, который инкапсулирует их собственную бизнес-логику. Я исследовал два подхода, но оба они были неуклюжими, и это наводит меня на мысль, неправильно ли я подхожу к проблемеи есть лучший способ использовать Java.

Вариант 1

Я думал об определении универсального интерфейса:

public interface ShapeInput<T> {
  T getInput();
}

Пользователь может затемсоздать:

public final class MyCircleInput<CircleInput> {

  private final ShapeDependency shapeDependency;

  @Inject
  MyCircleInput(ShapeDependency shapeDependency) {
    this.shapeDependency = shapeDependency;
  }

  @Override
  public CircleInput getInput() {
    return createCircleInput(shapeDependency);
  }

  // ... very complex business logic ...
  private static CircleInput createCircleInput(
      ShapeDependency shapeDependency) {
    // returns a CircleInput
  }
}

, а затем использовать ShapeFactory для создания правильного экземпляра на основе типа. Но я не могу сказать ShapeInput<CircleInput | TriangleInput>, и для принудительного выполнения этого поведения требуются проверки во время выполнения.

Опция 2

Я мог бы использовать наследование на Shape напрямую:

public abstract class AbstractShape implements Shape {

  protected final Shape shapeImpl;

  public AbstractShape(CircleInput input) {
    this.shapeImpl = new Circle(input);
  }

  public AbstractShape(TriangleInput input) {
    this.shapeImpl = new Triangle(input);
  }

  // Proxies print() to the underlying impl.
  @Override
  public void print() {
    return shapeImpl.print();
  }
}

и пользователи могут создавать:

public final MyCircle extends AbstractShape {

  @Inject
  MyCircle(ShapeDependency shapeDependency) {
    super(createCircleInput(shapeDependency));
  }

  // ... very complex business logic ...
  private static CircleInput createCircleInput(
      ShapeDependency shapeDependency) {
    // returns a CircleInput
  }
}

1 Ответ

0 голосов
/ 11 октября 2019

Итак Circle, Square, Triangle (которые все Shape с) и их соответствующие входы CircleInput, SquareInput и TriangleInput (которые не пересекаются) уже четко определеныв этой системе?

Почему пользователи вообще должны расширять какие-либо из этих входных данных?

Не является ли ваш конвейер по существу тем, что какая-то бизнес-логика воздействует на ShapeDependency, чтобы создать ShapeInput и передать его? на правильный Shape?

Вы можете уточнить это, четко определив ответственность каждого класса. Я ожидаю, что единственная ответственность CircleInput, SquareInput и т. Д. Будет заключаться в том, чтобы просто хранить входные данные, , а не для выполнения бизнес-логики.

Я бы оставил эту бизнес-логику в своей собственнойкласс, может быть что-то вроде этого:

abstract class BusinessLogic<S extends Shape, I> {
    private final Function<I, S> shapeConstructor;

    public BusinessLogic(Function<I, S> shapeConstructor) {
        this.shapeConstructor = shapeConstructor;
    }

    protected abstract I createShapeInput(ShapeDependency shapeDependency);

    public final S createShape(ShapeDependency shapeDependency) {
        I shapeInput = createShapeInput(shapeDependency);
        S shape = shapeConstructor.apply(shapeInput);
        return shape;
    }
}

class CircleBusinessLogic extends BusinessLogic<Circle, CircleInput> {

    public CircleBusinessLogic() {
        super(Circle::new);
    }

    @Override
    protected CircleInput createShapeInput(ShapeDependency shapeDependency) {
        return new CircleInput();
    }
}

Дайте мне знать, если это удовлетворяет вашим требованиям. Если нет, уточните, пожалуйста, ваши цели и направление.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...