如何遵循开闭原则扩展耦合的House与Window模型?
面向开闭原则的Window扩展方案选型
现有系统模型
中等规模系统中,以下模型代码已被多处引用:
public class House { public House(IEnumerable<Window> windows) { Windows = windows; } public IEnumerable<Window> Windows { get; private set; } } public class Window { public Window(string brand, int height, int width) { Brand = brand; Height = height; Width = width; } public string Brand { get; private set; } public int Height { get; private set; } public int Width { get; private set; } }
新增需求
系统部分模块需要判断Window是否具备SolarResistant(防紫外线)属性,但其他模块无需关心该属性。
已尝试的扩展方案及问题分析
方案1:继承Window实现子类
通过继承Window新增带IsSolarResistant属性的子类:
public class WindowMaybeSolarResistant : Window { public WindowMaybeSolarResistant(string brand, int height, int width, bool isSolarResistant) : base(brand, height, width) { IsSolarResistant = isSolarResistant; } public bool IsSolarResistant { get; private set; } }
问题:House类与Window高度耦合,系统中普遍默认House包含的是Window类型实例,子类无法直接替换使用。
方案2:为Window新增构造函数
直接修改Window类,新增带IsSolarResistant参数的构造函数:
public class Window : IWindow { public Window(string brand, int height, int width) { Brand = brand; Height = height; Width = width; IsSolarResistant = false; // 存在实际为true的窗口被误设为false的风险,容易误用构造函数 } public Window(string brand, int height, int width, bool isSolarResistant) { Brand = brand; Height = height; Width = width; IsSolarResistant = isSolarResistant; } public string Brand { get; private set; } public int Height { get; private set; } public int Width { get; private set; } public bool IsSolarResistant { get; private set; } }
问题:存在构造函数误用风险,且直接修改原有类违反开闭原则。
方案3:为House引入泛型与接口
定义IWindow抽象接口,将House改为泛型类:
public interface IWindow { public string Brand { get; } public int Height { get; } public int Width { get; } } public class House<TWindow> where TWindow : IWindow { public House(IEnumerable<TWindow> windows) { Windows = windows; } public IEnumerable<TWindow> Windows { get; private set; } }
优势:既保证了Window的基础属性约束,又为后续扩展预留了空间;完全符合开闭原则,无需修改原有Window和House的核心逻辑,只需新增实现IWindow的扩展类即可。
方案4:继承House类
通过继承House类,重写Windows属性类型:
public class HouseCouldHaveSolarWindows : House { public HouseCouldHaveSolarWindows(IEnumerable<WindowMaybeSolarResistant> windows) : base(windows) { } public new IEnumerable<WindowMaybeSolarResistant> Windows { get; private set; } }
问题:随着扩展需求增多,会导致类膨胀失控,维护成本急剧上升。
最佳方案推荐
方案3是最符合开闭原则的实现方式,理由如下:
- 通过
IWindow接口抽象窗口核心属性,彻底解耦House与具体Window类型的依赖; - 原有
Window类实现IWindow接口,无需修改原有业务逻辑,保证对修改关闭; - 当需要新增
SolarResistant属性时,只需新增实现IWindow的扩展类,原有模块不受影响,符合对扩展开放的要求; - 泛型
House<TWindow>可以适配任意实现IWindow的窗口类型,避免了类膨胀问题。
扩展实现示例
新增带防紫外线属性的窗口类:
public class SolarResistantWindow : IWindow { public SolarResistantWindow(string brand, int height, int width, bool isSolarResistant) { Brand = brand; Height = height; Width = width; IsSolarResistant = isSolarResistant; } public string Brand { get; private set; } public int Height { get; private set; } public int Width { get; private set; } public bool IsSolarResistant { get; private set; } }
使用时,需要该属性的模块可以直接使用House<SolarResistantWindow>,无需关心该属性的模块仍可使用原有House<Window>,完全隔离不同需求的模块。
内容的提问来源于stack exchange,提问作者jessiebot
相关产品推荐
相关产品推荐

