组合优于继承:如何实现组件的可扩展性?
嘿,我太懂你这种卡壳的感觉了——从多继承的思维切换到组合模式,确实得转个弯,尤其是扩展性这块,刚开始摸不着门道太正常了。让我结合你的场景,一步步拆解怎么把组合模式落地,还能让你的代码保持灵活扩展。
核心思路:用「has-a」替代「is-a」
多继承是让类成为多个基类的子类,而组合模式是让类持有多个独立组件的实例,把原本分散在基类里的行为,拆成一个个可复用的组件,然后在主类里把功能委托给这些组件。这样既避开了菱形继承的坑,还能轻松扩展。
第一步:把行为拆成独立的组件接口
先把你原来基类里的各类行为,抽象成单独的接口——每个接口对应一个单一职责的行为。比如假设你的SelectList原来继承了基础列表、筛选、排序的功能,那我们就拆成这几个组件接口:
// 基础列表行为的抽象 public interface IListCore { void AddItem(object item); object GetItem(int index); int ItemCount { get; } } // 筛选行为的抽象 public interface IFilterable { void ApplyFilter(string criteria); IEnumerable<object> GetFilteredItems(); } // 排序行为的抽象 public interface ISortable { void SortBy(string propertyName, bool ascending = true); }
第二步:实现可复用的组件
接下来针对每个接口,实现具体的组件——这些组件可以独立开发、测试和扩展,完全不依赖主类:
// 基础列表组件的实现 public class BasicListCore : IListCore { private readonly List<object> _items = new(); public void AddItem(object item) => _items.Add(item); public object GetItem(int index) => _items[index]; public int ItemCount => _items.Count; } // 简单筛选组件(依赖基础列表组件) public class SimpleFilter : IFilterable { private readonly IListCore _listCore; private IEnumerable<object> _filteredItems; public SimpleFilter(IListCore listCore) { _listCore = listCore; _filteredItems = Enumerable.Range(0, listCore.ItemCount) .Select(i => listCore.GetItem(i)); } public void ApplyFilter(string criteria) { _filteredItems = _filteredItems.Where(item => item.ToString().Contains(criteria)); } public IEnumerable<object> GetFilteredItems() => _filteredItems; } // 支持自定义比较器的高级排序组件 public class AdvancedSort : ISortable { private IEnumerable<object> _itemsToSort; public AdvancedSort(IEnumerable<object> items) { _itemsToSort = items; } public void SortBy(string propertyName, bool ascending = true) { var prop = typeof(object).GetProperty(propertyName); if (prop == null) return; _itemsToSort = ascending ? _itemsToSort.OrderBy(item => prop.GetValue(item)) : _itemsToSort.OrderByDescending(item => prop.GetValue(item)); } }
第三步:用组合模式重构你的主类
现在你的SelectList不再需要继承多个基类,而是通过持有这些组件的实例,把功能委托给它们。而且为了保证扩展性,我们可以用依赖注入的方式传入组件,或者提供默认实现:
public class SelectList { // 持有组件实例,用接口类型保证灵活性 private readonly IListCore _listCore; private readonly IFilterable _filter; private readonly ISortable _sort; // 构造函数:允许外部传入自定义组件,没有的话用默认实现 public SelectList(IListCore listCore = null, IFilterable filter = null, ISortable sort = null) { _listCore = listCore ?? new BasicListCore(); _filter = filter ?? new SimpleFilter(_listCore); _sort = sort ?? new AdvancedSort(_filter.GetFilteredItems()); } // 对外暴露的方法,直接委托给对应的组件 public void AddItem(object item) => _listCore.AddItem(item); public object GetItem(int index) => _listCore.GetItem(index); public void ApplyFilter(string criteria) => _filter.ApplyFilter(criteria); public void SortBy(string propertyName) => _sort.SortBy(propertyName); // 扩展性关键:允许动态替换组件 public void UpdateFilterComponent(IFilterable newFilter) => _filter = newFilter; public void UpdateSortComponent(ISortable newSort) => _sort = newSort; }
怎么实现可扩展性?
这才是组合模式的优势所在,你可以通过这几种方式轻松扩展:
- 新增行为:比如你想给
SelectList加搜索功能,只需要定义ISearchable接口,实现BasicSearch/AdvancedSearch组件,然后在SelectList里加对应的成员和委托方法就行,完全不用修改原有代码。 - 替换现有行为:如果觉得
SimpleFilter不够用,你可以写一个RegexFilter实现IFilterable,然后通过UpdateFilterComponent方法直接替换掉原来的筛选组件,主类的逻辑完全不用动。 - 组件复用:同一个
AdvancedSort组件,不仅可以用在SelectList里,还能直接用到DropdownList、DataGrid等其他需要排序的类里,比继承的复用性强太多。
小提醒
- 尽量用接口定义组件,别直接依赖具体类,这样替换和扩展才灵活。
- 组件之间可以互相依赖(比如
SimpleFilter依赖IListCore),但一定要依赖抽象,别依赖具体实现。 - 可选行为可以在构造函数里允许传入
null,然后提供默认实现,或者在需要的时候再初始化。
内容的提问来源于stack exchange,提问作者Konzy262
相关产品推荐
相关产品推荐

