『7x24小时有问必答』

生成器模式(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 都返回 Panel

public 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/Mutuals

2. 选择"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内部维护选中状态             ListBox

4. 点击Plot按钮         →    触发btPlot_Click事件                Form1

                                →    调用mchoice.getSelected()         ListChoice

                                →    从ListBox提取选中项                   ListChoice

                                →    返回ArrayList给Form1                 

                                →    显示消息框展示结果                     MessageBox

5. 切换选择"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, Misrosoft

eq.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 Bonds

eq.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)
1.webp
</string></string></string></type,></type,></abstract></interface>

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

上一主题上一主题         下一主题下一主题
QQ手机版小黑屋粤ICP备17165530号

关于我们·投诉举报· 用户帮助· 联系我们 · 本站服务 · 版权声明· 隐私政策 · 投搞指南

法律保护:PLC技术网,plcjs.com,plcjs.net等字样
Copyright 2010-2030. All rights reserved. 


微信公众号二维码 抖音二维码 百家号二维码 今日头条二维码哔哩哔哩二维码