可选组件功能与 SRP

Posted

技术标签:

【中文标题】可选组件功能与 SRP【英文标题】:Optional component functionality vs SRP 【发布时间】:2011-09-15 08:59:07 【问题描述】:

我目前遇到了一个设计问题。

假设有一个组件的层次结构。这些组件中的每一个都派生自一个抽象的Component 类型,看起来像这样:

public abstract class Component

    public abstract Component Parent  get; 
    public abstract ComponentCollection Children  get; 

现在我想为这些组件添加一些可选功能,让我们以能够在组件层次结构中搜索并在层次结构中选择组件为例。

像这样在基类中提供这些可选功能是否被认为是不好的做法:

public abstract class Component

    // Other members

    public abstract bool IsSearchable  get; 
    public abstract bool Search(string searchTerm);

    public abstract bool IsSelectable  get; 
    public abstract bool Select();

虽然“搜索能力”和“选择能力”在派生组件中由例如使用策略模式?

在我看来,这似乎违反了 SRP,但在我看来,唯一的选择是为每个可选功能提供一个接口,并且只在支持此功能的组件上实现它。

在我看来,这样做的缺点是每次我想检查组件是否提供特定功能时都必须编写这样的代码:

public bool Search(Component component, string searchTerm)

    ISearchable searchable = component as ISearchable;
    if(searchable != null)
    
        searchable.Search(searchTerm);
    

您会选择哪种策略,或者您有什么更好的想法?

提前致谢!

【问题讨论】:

为什么你指出的缺点与你当前的代码有很大的不同,它必须首先检查 component.IsSearchable()? 嗯,这是我不确定的。就我个人而言,我会考虑检查属性比检查对象的类型更干净(并且可能更快)。 从概念上讲,对象的类型是其属性之一(一般意义上),所以这不是问题。性能可能是一个问题,可能会使用 C++ 之类的语言,但可能不会使用 Java 或 C#。还要考虑拥有 ISearchable 接口的好处 - 您可能也拥有可搜索的非组件。 @MadKeithV:这是真的,但在某些情况下,有人应该知道她可以将Component 转换为ISearchable。在我看来,这不是很好的声明性。 @Florian - 他们可以查看类声明并看到 Component 是从 ISearchable 派生的(如果所有组件都具有 ISearchable 的默认实现)。这与查看类接口查看成员函数 IsSearchable 等没有什么不同。 【参考方案1】:

一个可能的选择:

如果正如您所说,通过策略模式(依赖注入)提供可搜索性/可选择性实现,那么我认为 ISearchable 和 ISelectable 的接口是一个更好的主意。

您可以从这些接口派生出您的策略对象,并在您的基本组件类中为它们实现 getter - GetSearchable()、GetSelectable() - 其中 Component 中的默认实现返回 null(或不喜欢 null 的接口)。

【讨论】:

谢谢,对我来说似乎是合理的。我会尝试这样做。【参考方案2】:

你为什么不用装饰器?

Component c = new Component ();
var selectableAndSearchableOne = new SelectableComponent (new SearchableComponent (c));

【讨论】:

我非常喜欢这个想法,但是在使用 Component 时如何访问选择/搜索功能? 此解决方案的一个潜在缺点是它必须在编译时工作 - 即 SearchableComponent 必须可以从 Component 构造。在所呈现的情况下,这听起来不是问题,但我可以想象在编译时您不知道“c”是否实际上是可搜索/可选择类型的场景。另一方面,如果你真的想强制所有组件至少是默认可搜索和可选择的,那么当你犯错时出现编译时错误是积极的,而不是消极的。 我没有看到任何缺点。您提到的请求“我想要可搜索的组件”我认为这是一个设计泄漏。它消除了复杂性。这不是组件的责任(位于更高级别的层)。【参考方案3】:

另一个好:这次你也知道了组件的扩展点。具有类似访客的模式

public interface IHasExtensions
    
        List<Extension> Extensions  get; 
        void Extend (Extension ext);
    

    public class Component : IHasExtensions
    
        List<Extension> exts = new List<Extension> ();

        public List<Extension> Extensions
        
            get  return exts; 
        

        public void Extend (Extension ext)
        
            exts.Add (ext);
        

        void Draw()  
    

    public abstract class Extension
    
        readonly protected Component _Component;

        public Extension(Component component)
        
            _Component = component;
        
    

    public class SearchExtension : Extension
    
        public SearchExtension (Component component) : base (component)
        

        
    

    public class SelectionExtension : Extension
    
        public SelectionExtension (Component component) : base (component)
        

        
    

    public class test_fly
    
        void start ()
        
            Component c = new Component ();
            c.Extend (new SearchExtension (c));
            c.Extend (new SelectionExtension (c));

            var exts = c.Extensions; // I Know the extensions now
        
    

【讨论】:

以上是关于可选组件功能与 SRP的主要内容,如果未能解决你的问题,请参考以下文章

nz-table复选功能改造(整行可选)

WinRT和可选参数发行

设计模式原则

drf 其他功能组件 - 限流-过滤-排序-分页-异常处理-生成接口文档-Xadmin

Angular搭建与分析

Sencha Touch 原生可选功能与所需权限(Nexus 7 上的相机)