问题的提出:

有些类很容易创建对象,直接调用其构造方法,例如Student student = new Student(“1001”,”zhang”,21); 之所以容易创建,因为其类成员都是基本数据类型或者封装类,或者字符串。但是如果对象的类成员还是对象,那么创建这个对象还需要产生该对象成员的具体对象。

public class Unit1 {
}
public class QuestionProduct {
Unit1 u1;
Unit2 u2;
Unit3 u3;
public void createUnit1(){
u1 = new Unit1();
}
public void createUnit2(){
u2 = new Unit2();
}
public void createUnit3(){
u3 = new Unit3();
}
public void composite(){ }
public static void main(String[] args) {
QuestionProduct p = new QuestionProduct();
p.createUnit1();
p.createUnit2();
p.createUnit3();
p.composite();
}
}

Unit123为各java对象,在main方法可以知道,只有当运行完p.composite()方法后,Product才真正的创建起来,问题来了,如果有两类Product对象,又或许有很多类成员。又或者随着Product产品种类的增加和减少,必须修改已有的源代码。于是为了解决这类问题,生成器模式应运而生!

生成器模式的主要思路是:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。简单来说,不在同一类里面创建该类的类成员,而是把类成员的创建交给另一个类,该类就叫做生成器!

public interface IBuild {
public Product create();
}
public class BuildProduct implements IBuild {
Product p = new Product(); public void createUnit1(){
//创建u1
}
public void createUnit2(){
//创建u2
}
public void createUnit3(){
//创建u3
}
public Product composite(){
//关联Unit1,Unit2,Unit3
return p;
}
public Product create(){
createUnit1();
createUnit2();
createUnit3();
return composite();
}
}

通过上面的代码可以知道,如果需求分析发生变化,只需要增加或者删除相应的生成器类BuildProduct即可,并不需要修改已有的类代码。

在这基础上,再定义一个调度类,是对生成器接口的IBuild的封装。

public class Director {
private IBuild iBuild;
public Director(IBuild iBuild){
this.iBuild = iBuild;
}
public Product build(){
//System.out.println("test");
iBuild.createUnit1();
iBuild.createUnit2();
iBuild.createUnit3();
return iBuild.composite();
} public static void main(String[] args) {
IBuild iBuild = new BuildProduct();
Director director = new Director(iBuild);
Product p = director.build();
}
}

这样就构成生成器模式的一般模式了!一般分为以下三个步骤

1)定义产品类

2)定义n个生成器Build类

3)定义一个统一调度类Director类

对于Director的理解:与常规的接口相比,生成器接口IBuild是特殊的,它是一个流程控制接口。该接口中定义的方法必须依照某种顺序执行,一个都不能少。因此在程序中一个要体现出“流程”这一特点。而Director类的作用就是对“流程”的封装类,其中的build方法决定了具体的流程控制过程。

对于上面的生成器模式,假如要生成两种Product产品,一种需要三种过程,一种需要四种过程,那么用上面的生成器模式(Model1)就不行了,因为它要求创建产品的过程必须相同(Interface IBuild定义好了创建的过程)。于是引起下面Model2的设计。

Model2:IBuild接口仅仅定义多态create()方法

public interface IBuild {
public Product create();
}

而在具体生成器类重写多态create()方法,并调用多个个非多态方法,最终返回Product对象。

public class BuildProduct implements IBuild {
Product p = new Product(); public void createUnit1(){
//创建u1
}
public void createUnit2(){
//创建u2
}
public void createUnit3(){
//创建u3
}
public Product composite(){
//关联Unit1,Unit2,Unit3
return p;
}
public Product create(){
createUnit1();
createUnit2();
createUnit3();
return composite();
}
}

Director类

public class Director {
private IBuild iBuild;
public Director(IBuild iBuild){
this.iBuild = iBuild;
}
public Product build(){
return iBuild.create();
}
}

对代码进行仔细分析可以发现,具体生成器多态的create()方法中包含了创建Product对象的全过程,Director类中的方法就显得重复了。在这种设计中其实是可以省略Director类的,这就说明了在生成器模式中,抽象生成器和具体生成器是必须的。

而指挥类需要在实际问题中认真考虑,加以取舍!进一步思考,可以把IBuild定义成泛型接口,不仅仅是Product产品,也可以是其他需要生成器魔术的产品都可以从这接口派生。

除了以上两种实现方式,还有第三种生成器功能的设计模式。

Model3:利用Product派生方法,可以实现类似生成器功能

具体代码如下

1.Product类

public abstract class Product {
Unit1 u1;
Unit2 u2;
Unit3 u3;
abstract void createUnit1(); //表明子类要创建Unit1,2,3,并组合它
abstract void createUnit2();
abstract void createUnit3();
abstract void composite();
}

2.生成器BuildProduct类

public class BuildProduct extends Product {
void createUnit1(){}
void createUnit2(){}
void createUnit3(){}
void composite(){}
}

3.指挥者类Director

public class Director {
Product p;
public Director(Product p){
this.p = p;
}
void build(){
p.createUnit1();
p.createUnit2();
p.createUnit3();
p.composite();
}
}

总而言之,对于生成器模式创建复制对象而言,主要的原则还是对象构建过程与表示相分离。这是一个总的思想,实现具体形式不是一成不变,大可以设计自己专属的生成器模式框架。重要的是思想,而不是实现形式!

最后再说说生成器模式的应用场景,其中一个比较重要的作用就是解决同流程,异界面的手段之一。

例如在登录下常常都会分不同的角色,比如教务系统登录区分教师和学生。一般的应用也分为管理员以及普通用户。不同的角色登录后或显示不同的页面。下面就教学管理系统这个例子简单说明。

        

学生的具体信息在student表中,教师的具体信息在teacher表中。一个常用的功能是:管理员为学生和教师在login表中分配了用户名和账号,同时在student和teacher表中建立关键字user的记录。但是其他具体信息如姓名年龄等是空的。

因此需要学生或者教师在登录后首先完善个人信息。下面正是利用生成器模式设计“个人信息完善”的基础代码。

Mysql 表的简单设计

create table student (
User varchar(20),
name varchar(20),
age int,
major varchar(20),
depart varchar(20)
)
insert student values('','张同学',21,'English','Computer');
insert student (user) values('');

1)界面抽象生成器UIBuilder

package BuildModel.Example;

import javax.swing.*;

/**
* Created by lenovo on 2017/4/18.
*/
public abstract class UIBuilder {
protected JPanel panel = new JPanel();
abstract public void addUI(); //形成界面
abstract public void registerMsg(); //注册消息
abstract public void initialData(String user); //初始化界面数据
public JPanel getPanel(){ //返回界面面板对象
return panel;
}
}

2)具体学生界面生成器类StudentBuilder

package BuildModel.Example;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Vector; /**
* Created by lenovo on 2017/4/18.
*/
public class StudentBuilder extends UIBuilder implements ActionListener {
String user;
JTextField studName = new JTextField(10); //姓名
JTextField studAge = new JTextField(10); //年龄
JTextField studMajor = new JTextField(10); //专业
JTextField studDepart = new JTextField(10); //学院
JButton updateBtn = new JButton("更新"); //该按钮需注册时间 public void addUI(){
JPanel center = new JPanel();
JPanel south = new JPanel();
Box b = Box.createVerticalBox(); //第1列垂直box对象b
b.add(new JLabel("姓名")); b.add(Box.createVerticalStrut(8));
b.add(new JLabel("年龄")); b.add(Box.createVerticalStrut(8));
b.add(new JLabel("专业")); b.add(Box.createVerticalStrut(8));
b.add(new JLabel("学院")); b.add(Box.createVerticalStrut(8));
Box b2 = Box.createVerticalBox(); //第2列垂直Box对象b2
b2.add(studName); b2.add(Box.createVerticalStrut(8));
b2.add(studAge); b2.add(Box.createVerticalStrut(8));
b2.add(studMajor); b2.add(Box.createVerticalStrut(8));
b2.add(studDepart); b2.add(Box.createVerticalStrut(8)); center.add(b);
center.add(b2); //center面板 = b + b2
south.add(updateBtn); //south面板 = updateBtn
panel.setLayout(new BorderLayout());
panel.add(center,BorderLayout.CENTER);
panel.add(south,BorderLayout.SOUTH); }
public void registerMsg(){
updateBtn.addActionListener(this);
}
public void initialData(String user1){ //界面数据显示初始化
this.user = user1;
String SQL = "select name,age,major,depart from student where user = '"
+ user + "'";
DbProc dbobj = new DbProc(); //数据库操作类
try{
dbobj.connect();
Vector l = (Vector) dbobj.executeQuery(SQL);
Vector v = (Vector)l.get(0);
studName.setText((String)v.get(0));
studAge.setText((String)v.get(1));
studMajor.setText((String)v.get(2));
studDepart.setText((String)v.get(3));
dbobj.close();
}
catch (Exception ex){
ex.printStackTrace();
}
}
public void actionPerformed(ActionEvent arg0){ //获得界面数据+更新数据库
String name = studName.getText();
String major = studMajor.getText();
String age = studAge.getText();
String depart = studDepart.getText();
String strSQL = "update student set name='" + name + "',age=" + age
+ ",major='" + major + "',depart='" + depart + "'" +
"where user='" + user + "'";
try{
DbProc dbProc = new DbProc();
dbProc.connect();
dbProc.executeUpdate(strSQL);
dbProc.close();
}
catch (Exception ex){
ex.printStackTrace();
}
}
}

3)DbProc数据库自定义封装类(注意测试时候strPwd要加上自己本地mysql的账户密码)

package BuildModel.Example;

import java.sql.*;
import java.util.List;
import java.util.Vector; /**
* Created by lenovo on 2017/4/18.
*/
public class DbProc {
private String strDriver = "com.mysql.jdbc.Driver";
private String strDb = "jdbc:mysql://localhost:3306/buildModel";
private String strUser = "root";
private String strPwd = ""; //注意测试时候strPwd要加上自己本地mysql的账户密码
private Connection conn;
public Connection connect() throws Exception{
Class.forName(strDriver);
conn = DriverManager.getConnection(strDb,strUser,strPwd);
return conn;
}
public int executeUpdate(String strSQL) throws Exception{
Statement stm = conn.createStatement();
int n = stm.executeUpdate(strSQL);
stm.close();
return n;
}
public List executeQuery(String strSQL) throws Exception{
List l = new Vector();
Statement stm = conn.createStatement();
ResultSet rst = stm.executeQuery(strSQL);
ResultSetMetaData rsmd = rst.getMetaData();
while(rst.next()){
Vector unit = new Vector();
for(int i=1;i<=rsmd.getColumnCount();i++){
unit.add(rst.getString(i));
}
l.add(unit);
}
return l;
}
public void close() throws Exception{
conn.close();
}
}

4)具体教师界面生成器TeacherBuilder(类似,这里就不写了)

5)流程指挥类Director

package BuildModel.Example;

import javax.swing.*;

/**
* Created by lenovo on 2017/4/18.
*/
public class Director {
private UIBuilder builder;
public Director(UIBuilder builder){
this.builder = builder;
}
public JPanel build(String user){
builder.addUI(); //初始化界面
builder.registerMsg(); //登记消息
builder.initialData(user); //填充账号为user的初始界面显示数据
return builder.getPanel();
}
}

6)测试类

package BuildModel.Example;

import javax.swing.*;

/**
* Created by lenovo on 2017/4/18.
*/
public class MyTest {
public static void main(String[] args) {
JFrame frm = new JFrame();
UIBuilder ub = new StudentBuilder(); //创建学生生成器
Director director = new Director(ub); //为学生生成器创建指挥者
JPanel panel = director.build("1001"); //指挥者建造张同学的更新界面
frm.add(panel);
frm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frm.pack();
frm.setVisible(true);
}
}

当然这只是简单的测试代码,在实际应用中还要注意很多问题。

生成器模式就讲到这里,觉得文章写得不错大家多多支持,如果觉得有什么不足希望能在评论区提出!

Java设计模式:生成器模式的更多相关文章

  1. Java设计模式——组合模式

    JAVA 设计模式 组合模式 用途 组合模式 (Component) 将对象组合成树形结构以表示“部分-整体”的层次结构.组合模式使得用户对单个对象和组合对象的使用具有唯一性. 组合模式是一种结构型模 ...

  2. java设计模式--单列模式

    java设计模式--单列模式 单列模式定义:确保一个类只有一个实例,并提供一个全局访问点. 下面是几种实现单列模式的Demo,每个Demo都有自己的优缺点: Demo1: /** * 单列模式需要满足 ...

  3. 3.java设计模式-建造者模式

    Java设计模式-建造者模式 在<JAVA与模式>一书中开头是这样描述建造(Builder)模式的: 建造模式是对象的创建模式.建造模式可以将一个产品的内部表象(internal repr ...

  4. Java设计模式-代理模式之动态代理(附源代码分析)

    Java设计模式-代理模式之动态代理(附源代码分析) 动态代理概念及类图 上一篇中介绍了静态代理,动态代理跟静态代理一个最大的差别就是:动态代理是在执行时刻动态的创建出代理类及其对象. 上篇中的静态代 ...

  5. Java设计模式——外观模式

    JAVA 设计模式 外观模式 用途 外观模式 (Facade) 为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用. 外观模式是一种结构型模式. 结构

  6. 【设计模式】Java设计模式 -工厂模式

    [设计模式]Java设计模式 -工厂模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长! 目 ...

  7. 【设计模式】Java设计模式 - 原型模式

    [设计模式]Java设计模式 - 原型模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 原创作品,更多关注我CSDN: 一个有梦有戏的人 准备将博客园.CSDN一起 ...

  8. 【设计模式】Java设计模式 - 桥接模式

    [设计模式]Java设计模式 - 桥接模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 原创作品,更多关注我CSDN: 一个有梦有戏的人 准备将博客园.CSDN一起 ...

  9. 【设计模式】Java设计模式 - 组合模式

    Java设计模式 - 组合模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 原创作品,更多关注我CSDN: 一个有梦有戏的人 准备将博客园.CSDN一起记录分享自己 ...

  10. 【设计模式】Java设计模式 - 外观模式

    Java设计模式 - 外观模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 原创作品,更多关注我CSDN: 一个有梦有戏的人 准备将博客园.CSDN一起记录分享自己 ...

随机推荐

  1. Android SDK教程

    Android SDK 网络问题解析 Android 客户端网络不稳定,会导致App 有时候无法及时收到 Push 消息. 很多开发者认为这是因为 JPush 推送不稳定.延迟,甚至有时候认为 JPu ...

  2. 本地计算机上的XXX服务启动后停止,某些服务在未由其它服务或程序使用时将自动停止

    创建WindowsService,以及安装和卸载网上的资料一搜一大堆,在这里就不再做演示,只说明下博主在工作中使用WindowsService服务出现的错误,以及最终的结局方案. 1.启动window ...

  3. Java程序员入门:程序员究竟可以干多少年?

    很多人都说程序员是青春饭,只能干到30岁. 然而事实真的如此么? 今天我们来探讨一下这个老话题,看看为了技术与编程执着究竟能走多远? 01年龄分布图 先来看一下程序员的年龄分布图: 我们可以看到程序员 ...

  4. SpringMVC:学习笔记(8)——文件上传

    SpringMVC--文件上传 说明: 文件上传的途径 文件上传主要有两种方式: 1.使用Apache Commons FileUpload元件. 2.利用Servlet3.0及其更高版本的内置支持. ...

  5. 巧用*_his表记录操作历史

    文章转载自「开发者圆桌」一个关于开发者入门.进阶.踩坑的微信公众号 许多OLTP应用的开发者都知道,一些重要的操作要记录操作历史,把操作前的数据备份到历史表,然后再执行相应的修改操作.这样可以获取某个 ...

  6. ionic的安装

    一.学习一样新的框架的步骤: 1.先找到人家的网站, 一个个点过来看看 2.我们前端的框架,分css与js 3.先学css 再学js 4.要学会复制黏贴代码, 实际演练代码的效果 二.ionic环境安 ...

  7. 1000: A+B Problem(NetWork Flow)

    1000: A+B Problem Time Limit: 1 Sec  Memory Limit: 5 MBSubmit: 11814  Solved: 7318[Submit][Status][D ...

  8. 1583: [Usaco2009 Mar]Moon Mooing 哞哞叫

    1583: [Usaco2009 Mar]Moon Mooing 哞哞叫 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 244  Solved: 126 ...

  9. spring mvc中的拦截器小结 .

    在spring mvc中,拦截器其实比较简单了,下面简单小结并demo下. preHandle:预处理回调方法,实现处理器的预处理(如登录检查),第三个参数为响应的处理器(如我们上一章的Control ...

  10. netty-all maven中 缺少jzlib

    在一个项目中引用 <dependency>          <groupId>io.netty</groupId>          <artifactId ...