伙计,这个答案确实增长了,但很有趣。开始了 :)
我同意这是一个相当复杂的设计。当您使用接口来“抽象变化的内容”时,接口很有帮助,但在该示例中,所有内容都是抽象的。这个例子真的很难理解,所以这应该是一个很大的指标,表明有问题。Spaghetti Code 的对立面是 Lasagna Code(多层),这正是这张图的内容。
从我可以看到你有3组。
让我们从 Nodes 开始,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
对于 Node Type,确实节点具有不同的类型,但它们仍然具有类型。我认为这不符合单独抽象的条件。我刚刚将 type 移到你的 basenode 中。
//This didn't add value so its just an enum used in the baseclass now
对于 Render,现在你正在做一些事情。由于 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
}