Человек, этот ответ действительно вырос, но это было весело.Вот и мы:)
Я согласен, что это довольно сложный дизайн.Интерфейсы полезны, когда вы используете их, чтобы «абстрагироваться от того, что меняется», но в этом примере все абстрагировано.Этому примеру очень трудно было следовать, так что это должно быть большим показателем того, что что-то не так.Противоположностью кода спагетти является код лазаньи (для многих слоев), и именно это и есть эта диаграмма.
Из того, что я вижу, у вас есть 3 группы.
- Узлы (дочерний, родительский и т. Д.)
- Тип узла
- Визуализация
Позволяет начать с узлов это правда, что SOLID говорит вам кодировать интерфейс, но это не обязательно означает, что это должен быть реальный интерфейс.Вполне допустимо использовать обычное старое наследование с дочерним узлом, который расширяет базовый узел.Это все еще Solid и имеет смысл в этом случае.
public class Node
{
public var NodeType { get; set; }
public var DisplayText { get; set; }
public IRenderable Renderer { get; set; }
public Node()
{
// TODO: Add constructor logic here
}
public void Render()
{
Renderer.Render();
}
}
public class ChildNode : Node
{
public var Owner {get; set;}
public var Sequence {get; set;}
public ChildNode()
{
NodeType = "Child"; //use an enum
DisplayText = "nom nom nom babies";
Renderer = new ChildRenderer();
}
}
//Parent Node is more of the same
Для типа узла верно, что узлы имеют разные типы, но оба они по-прежнему имеют тип.Я не думаю, что это квалифицирует это как отдельную абстракцию.Я только что переместил шрифт в ваш базовый узел.
//This didn't add value so its just an enum used in the baseclass now
Для рендера , теперь вы что-то делаете.Поскольку ChildNodes и ParentNodes отображаются по-разному, имеет смысл абстрагировать их.Хотя я вижу, что все свойства в IRenderer дублированы в IRenderContext, поэтому я просто собираю их в 1.
interface IRenderable
{
//properties
// TODO: Add properties here
//methods
void Render();
}
interface ChildRenderer : IRenderable
{
void Render()
{
//Render Me Here
}
}
//ParentRender() is more of the same
//I could add all sorts of functionallity with out touching other code
// ManChildRenderer(), TripletsChildRenderer()
И диаграмма классов будет выглядеть примерно такэтот.
Хорошо, это все хорошо, но зачем нужна вся эта дополнительная работа?Давайте посмотрим на окончательную реализацию.
public static void main()
{
//if this isnt acceptle instantiation use a NodeFactory
Node node1 = new ParentNode();
Node node2 = new ChildNode();
//Now we don't care about type -- Liskov Substitution Principle
node1.Render();
node2.Render();
//adding or changing functionality is now non-breaking
node1.Renderer = new ManChildRender();
//I've added a whole new way to render confident I didnt break the old renders
//In fact I didn't even upon those class files
}