|
生成器模式(Builder Pattern)实例详解 项目概述 本项目是一个投资选择管理系统,使用**生成器模式(Builder Pattern)**来动态创建不同的用户界面组件。根据金融产品类型的不同(股票、债券、基金),系统会自动选择合适的UI展示方式(复选框或列表框)。 --- 一、生成器模式核心概念 1.1 什么是生成器模式? 生成器模式是一种创建型设计模式,它将复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。 1.2 本项目的模式映射 生成器模式角色 | 本项目对应类 | 职责说明 | | Product(产品) | Panel | 最终构建的UI面板对象 | | Builder(抽象建造者) | MultiChoice 接口 | 定义构建UI的规范接口 | | Concrete Builder(具体建造者) | CheckChoice 、ListChoice | 实现不同的UI构建逻辑 | | Director(指挥者) | StockFactory | 控制建造者的选择和构建流程 | | Client(客户端) | Form1 | 使用最终产品的调用者 |
--- 二、代码结构分析 2.1 产品层(Product) Panel - UI容器 // 最终生成的产品是 Panel 对象// CheckChoice 和 ListChoice 都返回 Panelpublic Panel getWindow(){ return panel;}说明: Panel 是 Windows Forms 的容器控件,作为生成器模式的"产品",它封装了不同的UI组件(复选框或列表框)。 --- 2.2 抽象建造者(Builder Interface) MultiChoice 接口 public interface MultiChoice{ ArrayList getSelected(); // 获取用户选择的项目 void clear(); // 清空选择状态 Panel getWindow(); // 获取构建好的UI面板}设计意图: 定义了所有具体建造者必须实现的统一接口 确保不同的UI构建方式具有一致的行为契约 客户端(Form1)只需依赖此接口,无需关心具体实现 --- 2.3 具体建造者(Concrete Builders) 建造者1:CheckChoice(复选框建造者) 适用场景: 当金融产品数量 ≤ 3 时使用 public class CheckChoice : MultiChoice{ private ArrayList stocks; // 数据源 private Panel panel; // 产品容器 private ArrayList boxes; // 复选框集合 public CheckChoice(Equities stks) { stocks = stks.getNames(); panel = new Panel(); boxes = new ArrayList(); // 构建过程:为每个股票创建复选框 for (int i = 0; i < stocks.Count; i++) { CheckBox ck = new CheckBox(); ck.Location = new Point(0, 16 + i * 32); // 垂直排列 ck.Text = (string)stocks; ck.Size = new Size(112, 24); boxes.Add(ck); panel.Controls.Add(ck); // 添加到面板 } } public Panel getWindow() => panel; public ArrayList getSelected() { // 收集所有被勾选的复选框 ArrayList sels = new ArrayList(); for (int i = 0; i < boxes.Count; i++) { CheckBox ck = (CheckBox)boxes; if (ck.Checked) { sels.Add(ck.Text); } } return sels; } public void clear() { // 取消所有复选框的选中状态 for (int i = 0; i < boxes.Count; i++) { CheckBox ck = (CheckBox)boxes; ck.Checked = false; } }}构建特点: 适合少量选项(≤ 3个)的直观展示 用户可以一眼看到所有选项 支持多选操作 --- 建造者2:ListChoice(列表框建造者) 适用场景: 当金融产品数量 > 3 时使用 public class ListChoice : MultiChoice{ private ArrayList stocks; // 数据源 private Panel panel; // 产品容器 private ListBox list; // 列表框组件 public ListChoice(Equities stks) { stocks = stks.getNames(); panel = new Panel(); list = new ListBox(); // 配置列表框属性 list.Location = new Point(16, 0); list.Size = new Size(120, 160); list.TabIndex = 0; // 构建过程:填充列表项 for (int i = 0; i < stocks.Count; i++) { list.Items.Add(stocks); } panel.Controls.Add(list); } public Panel getWindow() => panel; public ArrayList getSelected() { // 获取列表中选中的项目 ArrayList sels = new ArrayList(); ListBox.SelectedObjectCollection coll = list.SelectedItems; for (int i = 0; i < coll.Count; i++) { sels.Add((string)coll); } return sels; } public void clear() { list.Items.Clear(); // 清空列表 }}构建特点: 适合大量选项的紧凑展示 支持滚动浏览 节省界面空间 --- 2.4 指挥者(Director) StockFactory 工厂类 public class StockFactory{ public static MultiChoice getBuilder(Equities stocks) { // 根据产品数量智能选择建造者 if (stocks.count() <= 3) { return new CheckChoice(stocks); // 使用复选框建造者 } else { return new ListChoice(stocks); // 使用列表框建造者 } }}指挥者的职责: 决策逻辑:根据输入条件(产品数量)决定使用哪个具体建造者 封装复杂性:客户端无需知道何时使用 CheckChoice 或 ListChoice 统一入口:提供静态方法作为建造者的获取点 设计亮点: 使用静态方法简化调用 内置智能判断逻辑(≤ 3 用复选框,> 3 用列表框) 返回抽象接口类型 MultiChoice,实现解耦 --- 2.5 客户端(Client) Form1 主窗体 public partial class Form1 : Form{ private ListBox lsEquities; // 左侧产品类型列表 private Button btPlot; // 确认按钮 private Panel pnl; // 右侧动态UI面板 private MultiChoice mchoice; // 当前使用的建造者 // 初始化:添加三种金融产品类型 private void init() { lsEquities.Items.Add(new Stocks()); // 5个股票 → 将使用 ListChoice lsEquities.Items.Add(new Bonds()); // 3个债券 → 将使用 CheckChoice lsEquities.Items.Add(new Mutuals()); // 4个基金 → 将使用 ListChoice } // 用户选择产品类型时触发 private void lsEquities_SelectedIndexChanged(object sender, EventArgs e) { int i = lsEquities.SelectedIndex; Equities eq = (Equities)lsEquities.Items; // 【关键步骤1】通过工厂获取合适的建造者 mchoice = StockFactory.getBuilder(eq); // 【关键步骤2】移除旧面板 this.Controls.Remove(pnl); // 【关键步骤3】获取新构建的面板 pnl = mchoice.getWindow(); // 【关键步骤4】显示新面板 setPanel(); } // 用户点击 Plot 按钮时 private void btPlot_Click(object sender, EventArgs e) { if (mchoice != null) { // 【关键步骤5】通过统一接口获取用户选择 ArrayList ar = mchoice.getSelected(); string ans = ""; for (int i = 0; i < ar.Count; i++) { ans += (string)ar + ""; } MessageBox.Show(null, ans, "Selected equities", MessageBoxButtons.OK); } }}客户端工作流程: 用户在左侧 ListBox 选择产品类型(Stocks/Bonds/Mutuals) 触发 SelectedIndexChanged 事件 调用 StockFactory.getBuilder() 获取合适的建造者 通过 getWindow() 获取构建好的 Panel 动态替换右侧面板显示 用户选择具体项目后,点击 Plot 按钮获取选择结果 --- 三、生成器模式工作过程详解 3.1 完整执行流程图 用户操作 系统响应 涉及组件─────────────────────────────────────────────────────────────────1. 启动程序 → Form1构造函数调用init() Form1 → 创建3个Equities对象并添加到列表 Stocks/Bonds/Mutuals2. 选择"Stocks" → 触发SelectedIndexChanged事件 Form1 → 获取Stocks对象(5个股票) Equities → 调用StockFactory.getBuilder() StockFactory → 判断: 5 > 3, 返回ListChoice ListChoice → ListChoice构造函数执行: ListChoice • 创建Panel容器 • 创建ListBox控件 • 填充5个股票名称到列表 → 移除旧Panel,添加新Panel Form1 → 显示ListBox界面给用户3. 用户勾选股票 → 在ListBox中选择多项 用户交互 → ListBox内部维护选中状态 ListBox4. 点击Plot按钮 → 触发btPlot_Click事件 Form1 → 调用mchoice.getSelected() ListChoice → 从ListBox提取选中项 ListChoice → 返回ArrayList给Form1 → 显示消息框展示结果 MessageBox5. 切换选择"Bonds" → 触发SelectedIndexChanged事件 Form1 → 获取Bonds对象(3个债券) Equities → 调用StockFactory.getBuilder() StockFactory → 判断: 3 ≤ 3, 返回CheckChoice CheckChoice → CheckChoice构造函数执行: CheckChoice • 创建Panel容器 • 创建3个CheckBox控件 • 垂直排列复选框 → 移除旧Panel,添加新Panel Form1 → 显示CheckBox界面给用户3.2 关键时序图 Form1 StockFactory ListChoice/CheckChoice Panel │ │ │ │ │──getBuilder()──→│ │ │ │ │──new Builder()──→│ │ │ │ │──构建UI组件──→│ │ │ │←─返回Panel────│ │ │←─返回Builder────│ │ │←─返回Builder────│ │ │ │ │ │ │ │──getWindow()──────────────────────→│ │ │←─返回Panel────────────────────────│ │ │ │ │ │ │──显示Panel────────────────────────────────────────────→│3.3 实际案例演示 案例1:选择 Stocks(5个股票) // 用户点击 "Stocks"Equities eq = new Stocks(); // array包含: Sisco, Coca Cola, GE, Harley Davidson, IBM, Misrosofteq.count() = 6// StockFactory 决策if (6 <= 3) { return new CheckChoice(stocks); // 不执行} else { return new ListChoice(stocks); // 执行}// ListChoice 构建过程Panel panel = new Panel();ListBox list = new ListBox();list.Items.Add("Sisco");list.Items.Add("Coca Cola");list.Items.Add("GE");list.Items.Add("Harley Davidson");list.Items.Add("IBM");list.Items.Add("Misrosoft");panel.Controls.Add(list);// 最终显示:一个可滚动的列表框,包含6个股票名称案例2:选择 Bonds(3个债券) // 用户点击 "Bonds"Equities eq = new Bonds(); // array包含: CT GO 2005, NY GO 2012, GE Corp Bondseq.count() = 3// StockFactory 决策if (3 <= 3) { return new CheckChoice(stocks); // 执行} else { return new ListChoice(stocks); // 不执行}// CheckChoice 构建过程Panel panel = new Panel();CheckBox ck1 = new CheckBox(); ck1.Text = "CT GO 2005"; ck1.Location = (0, 16);CheckBox ck2 = new CheckBox(); ck2.Text = "NY GO 2012"; ck2.Location = (0, 48);CheckBox ck3 = new CheckBox(); ck3.Text = "GE Corp Bonds"; ck3.Location = (0, 80);panel.Controls.Add(ck1);panel.Controls.Add(ck2);panel.Controls.Add(ck3);// 最终显示:3个垂直排列的复选框,用户可一目了然看到所有选项--- 四、设计模式优势分析 4.1 为什么使用生成器模式? 优势1:构建逻辑与表示分离 // 客户端只关心获取Panel,不关心如何构建pnl = mchoice.getWindow();// 具体构建细节封装在具体建造者中// CheckChoice: 创建CheckBox// ListChoice: 创建ListBox好处: Form1 不需要知道UI是如何构建的 修改UI构建逻辑不影响客户端代码 --- 优势2:相同的构建过程,不同的表示 // 无论使用哪种建造者,调用方式完全一致mchoice = StockFactory.getBuilder(eq); // 获取建造者pnl = mchoice.getWindow(); // 获取产品ArrayList selected = mchoice.getSelected(); // 获取结果好处: 统一的接口简化了客户端代码 可以轻松添加新的建造者(如 RadioButtonChoice) --- 优势3:灵活的对象创建 // StockFactory 根据条件动态选择建造者public static MultiChoice getBuilder(Equities stocks){ if (stocks.count() <= 3) return new CheckChoice(stocks); else return new ListChoice(stocks);}好处: 自动化决策,无需客户端干预 易于扩展新的决策规则 --- 优势4:符合开闭原则(Open-Closed Principle) 场景: 假设需要添加一种新的UI展示方式(如下拉框) // 只需添加新的具体建造者,无需修改现有代码public class DropDownChoice : MultiChoice{ private ComboBox combo; private Panel panel; public DropDownChoice(Equities stks) { // 构建下拉框UI panel = new Panel(); combo = new ComboBox(); // ... 填充数据 panel.Controls.Add(combo); } public Panel getWindow() => panel; public ArrayList getSelected() { /* ... */ } public void clear() { /* ... */ }}// 修改StockFactory的决策逻辑public static MultiChoice getBuilder(Equities stocks){ if (stocks.count() == 1) return new DropDownChoice(stocks); // 新增 else if (stocks.count() <= 3) return new CheckChoice(stocks); else return new ListChoice(stocks);}好处: 对扩展开放:可以添加新的建造者 对修改封闭:Form1 客户端代码无需改动 --- 4.2 与其他模式的对比 模式 | 用途 | 本项目为何不用 | | 工厂模式 | 创建单一产品 | 无法处理复杂的多步骤构建过程 | | 抽象工厂 | 创建产品族 | 过度设计,本项目只需构建单一Panel | | 生成器模式 | 分步骤构建复杂对象 | 完美匹配:需要逐步添加UI组件 | | 原型模式 | 克隆现有对象 | 不适用,每次都需要从头构建 |
--- 五、类关系图 ┌─────────────────────────────────────────────────────────────┐│ Client (Form1) ││ ┌──────────────┐ ┌──────────┐ ┌──────────────────┐ ││ │ lsEquities │ │ btPlot │ │ pnl (Panel) │ ││ │ (ListBox) │ │(Button) │ │ (动态UI容器) │ ││ └──────┬───────┘ └────┬─────┘ └────────┬─────────┘ ││ │ │ ▲ ││ └─────事件触发────┘ │ ││ │ ││ ┌───────────┴──────────┐ ││ │ <<interface>> │ ││ │ MultiChoice │ ││ │ ─────────────────── │ ││ │ +getWindow(): Panel │ ││ │ +getSelected(): ArrayList││ │ +clear(): void │ ││ └────┬───────────┬─────┘ ││ │ │ ││ ┌──────────┴──┐ ┌────┴──────┐ ││ │ CheckChoice │ │ListChoice │ ││ │ (≤3项时用) │ │ (>3项时用) │ ││ │ ─────────── │ │ ───────── │ ││ │ -boxes │ │ -list │ ││ │ +getWindow()│ │+getWindow()│ ││ └─────────────┘ └───────────┘ │└─────────────────────────────────────────────────────────────┘ ▲ │ 创建 ┌─────────┴──────────┐ │ StockFactory │ │ (Director/工厂) │ │ ───────────────── │ │ +getBuilder(): │ │ MultiChoice │ └─────────┬──────────┘ │ 使用 ┌─────────┴──────────┐ │ <<abstract>> │ │ Equities │ │ ───────────────── │ │ array: ArrayList │ │ +getNames() │ │ +count() │ │ +ToString() │ └────┬───────┬───────┘ │ │ ┌──────────┴──┐ ┌──┴────────┐ ┌──┴──────────┐ │ Stocks │ │ Bonds │ │ Mutuals │ │ (6个项目) │ │ (3个项目) │ │ (4个项目) │ └─────────────┘ └───────────┘ └─────────────┘--- 六、关键技术要点 6.1 ToString() 重写的重要性 // Equities 基类public override string ToString(){ return "Equities";}// Stocks 子类public override string ToString(){ return "Stocks"; // ListBox 显示此文本}注意: 必须使用 override 关键字,而非 abstract abstract 会导致 ListBox 调用 GetType().ToString(),显示完整类型名 WinFormsApp11.Stocks override 确保显示简洁的名称 Stocks --- 6.2 多态的应用 // Form1 中声明为接口类型private MultiChoice mchoice;// 运行时可以是不同的具体类型mchoice = new CheckChoice(stocks); // 或mchoice = new ListChoice(stocks);// 调用时自动执行对应版本的方法mchoice.getWindow(); // 多态调用mchoice.getSelected(); // 多态调用好处: 客户端代码无需判断具体类型,统一通过接口调用。 --- 6.3 动态UI替换 // 移除旧面板this.Controls.Remove(pnl);// 获取新面板pnl = mchoice.getWindow();// 设置位置并添加新面板setPanel();技术细节: Windows Forms 允许运行时动态添加/移除控件 通过 Controls 集合管理子控件 每次切换产品类型都会重建UI --- 七、扩展建议 7.1 可能的改进方向 改进1:缓存已构建的面板 // 当前实现:每次切换都重新构建pnl = mchoice.getWindow(); // 每次都new// 改进:缓存已构建的面板,避免重复创建private Dictionary<type, panel=""> panelCache = new Dictionary<type, panel="">();if (!panelCache.ContainsKey(eq.GetType())){ mchoice = StockFactory.getBuilder(eq); panelCache[eq.GetType()] = mchoice.getWindow();}pnl = panelCache[eq.GetType()];优点: 提升性能,保留用户之前的选择状态 --- 改进2:使用泛型增强类型安全 // 当前:返回 ArrayList(非类型安全)public ArrayList getSelected()// 改进:使用泛型列表public List<string> getSelected(){ List<string> sels = new List<string>(); // ... return sels;}优点: 编译时类型检查,避免运行时错误 --- 改进3:添加更多建造者 // 单选按钮建造者(适合互斥选择)public class RadioChoice : MultiChoice { /* ... */ }// 树形视图建造者(适合层级数据)public class TreeChoice : MultiChoice { /* ... */ }// 网格视图建造者(适合表格数据)public class GridChoice : MultiChoice { /* ... */ }--- 7.2 重构建议 建议1:将决策逻辑提取为策略模式 public interface IBuilderStrategy{ MultiChoice CreateBuilder(Equities stocks);}public class CountBasedStrategy : IBuilderStrategy{ public MultiChoice CreateBuilder(Equities stocks) { return stocks.count() <= 3 ? (MultiChoice)new CheckChoice(stocks) : new ListChoice(stocks); }}// StockFactory 使用策略public class StockFactory{ private IBuilderStrategy strategy; public StockFactory(IBuilderStrategy strategy) { this.strategy = strategy; } public MultiChoice getBuilder(Equities stocks) { return strategy.CreateBuilder(stocks); }}优点: 决策算法可独立变化和测试 --- 八、总结 8.1 生成器模式在本项目的核心价值 封装复杂构建过程 - UI组件的创建细节对客户端透明 提供灵活的表示 - 同样的数据可以用复选框或列表框展示 简化客户端代码 - Form1 只需调用统一接口 易于扩展维护 - 添加新UI类型不影响现有代码 8.2 学习要点 识别生成器模式的场景: 需要创建复杂对象(多个组成部分) 构建过程需要分步骤进行 同样的构建过程需要不同的表示 本项目的模式要素: Product: Panel Builder: MultiChoice 接口 ConcreteBuilder: CheckChoice, ListChoice Director: StockFactory Client: Form1 设计原则体现: 单一职责原则:每个建造者只负责一种UI构建 开闭原则:可扩展新的建造者 依赖倒置原则:客户端依赖抽象接口 里氏替换原则:具体建造者可互换 --- 附录:完整类清单 文件名 | 类名 | 角色 | 行数 | Equities.cs | Equities | 产品数据基类 | 29 | Stocks.cs | Stocks | 具体产品(股票) | 32 | Bonds.cs | Bonds | 具体产品(债券) | 23 | Mutuals.cs | Mutuals | 具体产品(基金) | 24 | MultiChoice.cs | MultiChoice | 抽象建造者接口 | 18 | CheckChoice.cs | CheckChoice | 具体建造者1 | 62 | ListChoice.cs | ListChoice | 具体建造者2 | 50 | StockFactory.cs | StockFactory | 指挥者/工厂 | 24 | Form1.cs | Form1 | 客户端 | 61 |
总计: 9个类,约323行代码,完整实现了生成器模式。 --- 文档生成时间:2026年4月5日项目名称:WinFormsApp11 - 投资选择管理系统设计模式:生成器模式(Builder Pattern) </string></string></string></type,></type,></abstract></interface> 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |