Мужик этот ответ действительно вырос, но было весело. Начнем :)
Согласен, это довольно сложный дизайн. Интерфейсы полезны, когда вы используете их для «абстрагирования того, что меняется», но в этом примере абстрагировано все. Этому примеру было действительно трудно следовать, так что это должно быть большим признаком того, что что-то не так. Противоположностью коду спагетти является код лазаньи (для многих слоев), и это именно то, что представляет собой эта диаграмма.
Как я вижу, у вас есть 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
}
person
Evan
schedule
18.03.2011