问题的提出:
有些类很容易创建对象,直接调用其构造方法,例如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('1001','张同学',21,'English','Computer'); insert student (user) values('1002');
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); } }
当然这只是简单的测试代码,在实际应用中还要注意很多问题。
生成器模式就讲到这里,觉得文章写得不错大家多多支持,如果觉得有什么不足希望能在评论区提出!