Пройдя мимо открытого-закрытого принципа

У меня есть простая программа, которая рисует геометрические фигуры на основе данных мыши, предоставленных пользователем. У меня есть один класс, который обрабатывает отслеживание мыши (он получает список с историей движения мыши) и один абстрактный класс под названием Shape. Из этого класса я извлекаю несколько дополнительных фигур, таких как круг, прямоугольник и т. Д., И каждый из них переопределяет абстрактную функцию Draw().

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

Я явно не хочу выпускать такой код:

List<Shape> Shapes = new List<Shape>();
// somwhere later 

if(CurrentShape == "polyline"){
    Shapes.Add(new Polyline(Points)); 
}
else if (CurrentShape == "rectangle"){
    Shapes.Add(new Rectangle(BeginPoint, EndPoint));
}
// and so on.

Код выше явно опровергает принцип Open-Closed. Проблема в том, что я не знаю, как с этим справиться. Основная проблема заключается в том, что разные Shapes имеют конструкторы с разными параметрами, что делает его намного более хлопотным.

Я уверен, что это общая проблема, но я не знаю, как ее обойти. У тебя есть идея?

2 ответа

Решение

Это просит о фабрике, а не только фабрике, но фабрике с инъекционными рабочими.

public class Context {
   public Point BeginPoint;
   public Point EndPoint;
   public List Points;

   whatever else
}

public class ShapeFactory {

   List<FactoryWorker> workers;

   public Shape CreateShape( string ShapeName, Context context )
   {
      foreach ( FactoryWorker worker in workers )
         if ( worker.Accepts( ShapeName ) )
             return worker.CreateShape( context );
   }

   public void AddWorker( FactoryWorker worker ) {
      workers.Add( worker );
   }
 }

 public abstract class FactortWorker {
    public abstract bool Accepts( string ShapeName );
    puboic Shape CreateShape( Context context );
 }

 public class PolyLineFactoryWorker : FactoryWorker {

    public override bool Accepts( string ShapeName ) {
       return ShapeName == "polyline";
    }

    public Shape CreateShape( Context context ) { ... }

 }

Таким образом, код открыт для расширений - новые фабричные рабочие могут свободно создаваться и добавляться на фабрику.

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

Один из способов его реализации заключается в следующем:

interface IShapeMaker {
    IShape Make(IList<Point> points);
}
class RectMaker : IShapeMaker {
    public Make(IList<Point> points) {
        // Check if the points are good to make a rectangle
        ...
        if (pointsAreGoodForRectangle) {
            return new Rectangle(...);
        }
        return null; // Cannot make a rectangle
    }
}
class PolylineMaker : IShapeMaker {
    public Make(IList<Point> points) {
        // Check if the points are good to make a polyline
        ...
        if (pointsAreGoodForPolyline) {
            return new Polyline(...);
        }
        return null; // Cannot make a polyline
    }
}

С этими Maker классы в руках, вы можете сделать реестр производителей (простой List<IShapeMaker>) пройти через создателей, передавая им точки и останавливаясь, когда вы возвращаете ненулевую форму.

Эта система остается расширяемой, потому что вы можете добавить пару NewShape а также NewShapeMakerи "подключите их" к существующей платформе: один раз NewShapeMaker попадает в реестр, остальная часть системы мгновенно становится готова распознать и использовать ваш NewShape,

Другие вопросы по тегам