如何通过构图包含属性?

时间:2018-03-27 11:01:16

标签: c# oop inheritance design-patterns composition

第三次必须在我当前的项目中重构继承链后,我用Google搜索"继承很糟糕"并且发现我遇到的问题并不罕见,并且组合是推荐的替代解决方案。

我理解你如何使用合成来以函数的形式添加行为,但是我有问题想出用同样的方法添加属性的方法。

让我们说我想建模树节点。每个节点至少有两个属性:名称和描述。

class Node {
   public string Name { get; set; }
   public string Description { get; set; }
}

其他更具体的节点将继承这些属性,如下所示:

class StructuredNode : Node {
   public List<Node> Children { get; set; }
}

如何在不依赖继承及其附带的问题的情况下实现属性代码的类似可重用性? 是否有这样的设计模式,或者在这种情况下是否必须使用继承?

提前致谢!

修改 位置&#34;组合优于继承的例子&#34;:

4 个答案:

答案 0 :(得分:2)

而不是依赖于类,你应该依赖儿子抽象(这也是使用构图的一部分)所以对你来说你应该这样做

public interface INode {
   string Name { get; set; }
   string Description { get; set; }
}

class Node : INode {
   public string Name { get; set; }
   public string Description { get; set; }
}

class StructuredNode : INode {
   public string Name { get; set; }
   public string Description { get; set; }
   public List<INode> Children { get; set; }
}

或者您也可以这样做

//this is something similar to decorator pattern.
class StructuredNode  {
   private readonly INode _node;
   public StructureNode(INode node)
   {
      _node = node;//now you can reuse function of any class which implements INode
   }

   public List<INode> Children { get; set; }
}

你也应该在以后

这样做
   List<Node> nodes = List<Node>();
   StructuredNode sNode = new StructuredNode();
   sNode.Children  = nodes; 

这是可能的,因为所有都是基于抽象。现在所有实现都使用INode

其他解决方案建议您在评论中使用Decorator pattern。如果你只是想扩展你的课而不修改它。

答案 1 :(得分:1)

  

如何在不依赖继承及其附带的问题的情况下归档属性代码的类似可重用性?

使用继承的替代方法是接口或组合。但是,对于特定属性,您有点卡住了。

  • 接口不能以与基类相同的方式包含默认实现。因此,虽然您可以强制您的类使用正确的&#34;组合属性结构&#34;,但您无法在可实现接口的每个类中实现可重用方法(或者您可以吗?休息后更多!
  • 组合根本不存在于C#中,您可以动态地向属性添加属性(除非您对Dictionary<string,string>感到满意)。可能有一些人为的方法从技术上使其成功,但它不会是一个好方法。

接口+扩展方法

此处可以使用扩展方法来替换您在继承的基类中找到的可重用逻辑。

这有一个缺点:您希望在扩展方法中访问的属性需要是接口合同的一部分,并且可以公开访问。
除了这个缺点之外,它还会根据您的其他要求打勾。

首先,基于继承的示例:

public class Property
{
    public string Name { get; set; }
    public string Value { get; set; }
}

public class PropertyComposedObject
{
    public List<Property> Properties { get; set; }

    public Property GetProperty(string name)
    {
        return this.Properties.SingleOrDefault(x => x.Name == name);
    }
}

public class Person : PropertyComposedObject
{

}

如果我们改为使用界面,我们将失去对共享GetNode(string)方法等好处的访问权限。您可以将其添加为界面的一部分,但每个实现类将负责实现该方法(导致您在整个位置复制/粘贴相同的方法)。

没有扩展方法的接口示例:

public class Property
{
    public string Name { get; set; }
    public string Value { get; set; }
}

public interface IPropertyComposedObject
{
    List<Property> Properties { get; set; }

    Property GetProperty(string name);
}

public class Person : IPropertyComposedObject
{
    public List<Property> Properties { get; set; }

    public Property GetProperty(string name)
    {
        return this.Properties.SingleOrDefault(x => x.Name == name);
    }
}

但扩展方法允许我们定义可重用的方法一次,但仍然可以从实现该接口的每个类访问它:

public class Property
{
    public string Name { get; set; }
    public string Value { get; set; }
}

public interface IPropertyComposedObject
{
    List<Property> Properties { get; set; }
}

public class Person : IPropertyComposedObject
{
    public List<Property> Properties { get; set; }
}

public static class IPropertyComposedObjectExtensions
{
    public Property GetProperty(this IPropertyComposedObject obj, string name)
    {
        return obj.Properties.SingleOrDefault(x => x.Name == name);
    }
}

答案 2 :(得分:0)

我尝试最小化代码重复:

interface INodeProperties
{
    string Name { get; set; }
    string Description { get; set; }
}

class NodeProperties : INodeProperties
{
    public string Name { get; set; }
    public string Description { get; set; }
}

interface INode
{
     INodeProperties NodeProps { get; set; }
}

class Node : INode
{
     public INodeProperties NodeProps { get; set; } = new NodeProperties();
}

interface IStructuredNode
{
      List<Node> Children { get; set; }
}

class StructuredNode: INode, IStructuredNode
{
     public INodeProperties NodeProps { get; set; } = new NodeProperties();
     public List<Node> Children { get; set; }
}

下行:再多一次&#34;跳&#34;到达实际的属性...... :(

答案 3 :(得分:0)

有一个INode接口,它封装了公共属性。

这样你应该有自动属性,然后避免将逻辑放在属性中。 getter和setter,因为你无法重用这个逻辑。

然后重复自动属性定义并不重要,不会影响可重用性。

如果您需要更改属性通知,最好使用postsharp等拦截器库。