• Java设计模式:生成器模式


    问题的提出:

    有些类很容易创建对象,直接调用其构造方法,例如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的设计。

    Model2IBuild接口仅仅定义多态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();
    }
    View Code

    2.生成器BuildProduct类

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

    3.指挥者类Director

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

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

     

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

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

            

    学生的具体信息在student表中,教师的具体信息在teacher表中。一个常用的功能是:管理员为学生和教师在login表中分配了用户名和账号,同时在studentteacher表中建立关键字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');
    View Code

    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();
            }
        }
    }
    View Code

    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();
        }
    }
    View Code

     

    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);
        }
    }
    View Code

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

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

  • 相关阅读:
    有序矩阵中的第 k 个最小数组和
    查找和最小的K对数字
    前 K 个高频元素
    621. 任务调度器
    407. 接雨水 II
    c语言表达式求值 中缀表达式转后缀表达式 求值
    42. 接雨水
    MySQL高级特性——绑定变量
    MySQL高级特性之分区表
    MySQL优化特定类型的查询
  • 原文地址:https://www.cnblogs.com/lateink/p/6730922.html
Copyright © 2020-2023  润新知