需要 .Net SOLID 设计方面的帮助

发布于 2024-09-14 17:35:31 字数 416 浏览 4 评论 0原文

我第一次尝试坚持罗伯特·马丁的 SOLID 设计原则,但我并不擅长。

本质上,我需要一个“Node”对象的层次结构。有些节点是 NodeHosts,有些是 NodeChildren,有些是 Both。每个人以前都做过这个,但我不知道如何在不过度复杂化设计或在节点子类型中做类似事情的情况下做到扎实:

 INodeHostType node;
 public INodeType NodeType
 {
 ....
      set 
      {
          node = (INodeHostType)value;
      }
 }

这违反了里氏替换原则,对吗?有什么更好的办法呢? 这就是我现在所拥有的。 替代文本

I'm trying to stick fast to Robert Martin's SOLID design principles for the first time, and I am not good at it.

In essence, I need a hierarchy of "Node" objects. Some nodes are NodeHosts, some are NodeChildren and some are Both. Everybody's done this one before, but I can't figure out how to do it SOLID without over-complicating the design or doing something like this in the node subtypes:

 INodeHostType node;
 public INodeType NodeType
 {
 ....
      set 
      {
          node = (INodeHostType)value;
      }
 }

This violates Liskov Substitution Principle right? What's a better way?
Here's what I have now.
alt text

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

甚是思念 2024-09-21 17:35:31

伙计,这个答案确实很丰富,但很有趣。我们开始吧:)

我同意这是一个相当复杂的设计。当您使用接口来“抽象变化的内容”时,接口会很有帮助,但在该示例中,所有内容都是抽象的。这个例子确实很难遵循,所以这应该是一个重要的指标,表明出现了问题。与 Spaghetti 代码相反的是 Lasagna 代码(多层),这正是该图的内容。

据我所知,你们有 3 个小组。

  • 节点(子节点、父节点等)
  • 节点类型
  • 渲染

让我们从节点开始,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

对于渲染,现在您正在做一些事情。由于 ChildNode 和 ParentNode 的渲染方式不同,因此对其进行抽象是有意义的。虽然,我看到 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()

类图看起来像这样。
Solid Example

好的,这一切都很好,但是为什么还要做所有额外的工作 Solid 呢?让我们看看最终的实现。

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
}

Man this answer really grew, but it was fun. Here we go :)

I agree that is a pretty complicated design. Interfaces are helpful when you use them to "abstract what varies", but in that example everything is abstracted. This is example was really difficult to follow, so that should be a big indicator that something is wrong. The opposite of Spaghetti Code is Lasagna Code (to many layers) and thats exactly what this diagram is.

From what I can see you have 3 groups.

  • Nodes (Child, Parent, etc)
  • Node type
  • Render

Lets start with Nodes, its true that SOLID tells you to code to an interface, but that doesn't necessarily mean it has to be an actual interface. It's perfectly acceptable to use regular old inheritance with a childnode that extends a basenode. That is still Solid and makes sense in this case.

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

For Node Type, its true that the nodes have different types but they both still have a type. I don't think this qualifies it for a separate abstraction. I have just moved type into your basenode.

//This didn't add value so its just an enum used in the baseclass now

For Render, now you are on to something. Since ChildNodes and ParentNodes are rendered differently it makes sense to abstract it. Although, I see is that all the properties in IRenderer are duplicated in IRenderContext so I'm just going to collapse them into 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()

And the Class Diagram would look something like this.
Solid Example

Ok so thats all well and good, but why do all the extra work solid requires. Lets look at the final implementation.

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
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文