Пройдя мимо открытого-закрытого принципа
У меня есть простая программа, которая рисует геометрические фигуры на основе данных мыши, предоставленных пользователем. У меня есть один класс, который обрабатывает отслеживание мыши (он получает список с историей движения мыши) и один абстрактный класс под названием 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
,