zoukankan      html  css  js  c++  java
  • 设计模式之工厂模式

    文章结构
    1.工厂模式的分类
    2.参考文章与书籍


    1.工厂模式的分类

    工厂模式分为简单工厂模式、工厂方法、抽象工厂三种。
    工厂所产生的对象叫产品。

    1.1简单工厂模式

    将创建对象的过程放入工厂,工厂根据接受的消息来决定创建的对象。直接上例子(普通写法和简单工厂模式写法):

    普通写法
    /**
     * 打印机A
     * @author live
     *
     */
    public class Printer_A{
    	private String date;
    	private String user;
    	
    	public Printer_A(String user,String date){
    		this.date = date; 
    		this.user = user;
    	}
    	
    	public void print(){
    		System.out.println("有人使用了"+user+"在"+date+"年买回来的打印机A");
    	}
    }
    
    public class Printer_B{
    	public void print(){
    		System.out.println("有人使用了打印机B");
    	}
    }
    public class Runsim{
    	public static void main(String[] args) {
    		Printer_A printer_A = new Printer_A("小明","2018");
    		printer_A.print();
    		
    		Printer_B printer_B = new Printer_B();
    		printer_B.print();
    	}
    }
    

    这是普通写法,代码中使用者拥有打印机对象的创建权,造成了代码高耦合。而且业务与逻辑也没有实现分离;其次Printer_A每次创建都需要重复的将打印机的购买者和日期放入,(不要问我为什么不直接将用户和日期写死,我只是为了模拟一下创建对象的复杂操作,因为没想到好例子,只能这么代替,大家就当这是一个特别复杂的流程就好了,/手动捂脸),而且在其他业务中,每次调用都要重新写这个操作流程,造成了代码的冗杂。

    简单工厂模式写法
    /**
     * 打印机接口
     * @author live
     *
     */
    public interface IPrinter{
    	void print();
    }
    
    public class Printer_AImpl implements IPrinter{
    	
    	private String date;
    	private String user;
    	
    	public Printer_AImpl(String user,String date){
    		this.date = date; 
    		this.user = user;
    	}
    	
    	@Override
    	public void print(){
    		System.out.println("有人使用了"+user+"在"+date+"年买回来的打印机A");
    	}
    }
    
    public class Printer_BImpl implements IPrinter{
    	@Override
    	public void print(){
    		System.out.println("有人使用了打印机B");
    	}
    }
    /**
     * 简单工厂模式-工厂
     * @author live Printer
     *
     */
    public class SimpleFactory {
    	// 根据用户输入的不同产生不同的产品
    	public static IPrinter createPrinter(String str){
    		IPrinter iPrinter = null;
    		switch(str){
    		case "1" :
    			iPrinter = new Printer_AImpl("小明","2018");
    			break;
    		case "2" :
    			iPrinter = new Printer_BImpl();
    			break;
    		default :
    			break;
    		}
    		return iPrinter;
    	}
    }
    
    public class RunPrinter{
    	public static void main(String[] args) {
    		IPrinter iPrinter = SimpleFactory.createPrinter("1");
    		iPrinter.print();
    	}
    }
    

    上面的代码先创建一个公共接口,由打印机A、B来实现接口,将对象的创建放入工厂的静态方法,消费者每次通过向静态方法传入对应信息来获取相应的对象,消费者只需要使用对象(控制反转)。

    因为示例简单,所以看不出简单工厂的好处,如果创建打印机A的过程更加复杂,需要很多的赋值操作,就能体现出简单工厂模式的好处了,而简单工厂也是适用于创建对象复杂的情况,如果只是new一个对象,就直接来new就好了。

    如果再添加打印机C,只需要让C实现接口,同时在工厂中添加新的分支就行,相应的业务层使用的时候传入C对应的信息即可。但是这样操作的话就违反了开闭原则(对修改关闭,多扩展开放),每次添加新的打印机的时候都需要去修改工厂的静态方法。

    优点:耦合度低,代码简洁、隐藏对象创建的细节
    缺点:扩展性差,每次添加新的类都要去改工厂的静态方法。

    1.2工厂方法

    工厂方法定义了工厂的接口,根据具体的工厂子类来决定创建的产品

    // 打印机和简单工厂模式的打印机一样,首先有打印机接口,然后实现具体的打印机,在这省去不写
    
    /**
     * 工厂接口
     * @author live
     *
     */
    public interface IFactory{
    	/**
    	 * 生产产品
    	 * @return
    	 */
    	IPrinter createPrinter();
    }
    /**
     * 生产A打印机的工厂
     */
    public class PrinterAFactoryImpl implements IFactory{
    
    	@Override
    	public IPrinter createPrinter() {
    		// 打印机和简单工厂模式的打印机一样,省去不写
    		return new Printer_AImpl("小明","2018");
    	}
    	
    }
    /**
     * 生产B打印机的工厂
     */
    public class PrinterBFactoryImpl implements IFactory{
    
    	@Override
    	public IPrinter createPrinter() {
    		return new Printer_BImpl();
    	}
    	
    }
    /**
     * 工厂方法
     * @author live
     *
     */
    public class RunFactoryMethod {
    	public static void main(String[] args) {
    		// 使用打印机A
    		IFactory factory = new PrinterAFactoryImpl();
    		IPrinter iPrinter =  factory.createPrinter();
    		iPrinter.print();
    	}
    }
    

    该模式将工厂抽象,通过创建指定的工厂子类来生产对应的产品。客户端只需要知道工厂即可。每次新增加打印机的时候只需要创建打印机和对应的工厂即可,遵循了开闭原则。但是增加了额外的开发量。每次增加新的产品也需要增加对应的工厂。

    其次,在业务层(使用类)中,如果按照简单工厂模式生产了10个打印机B,此时要求我们将打印机B全部改成打印机A,我们需要该10处代码,如果是工厂方法,我们只需要修改创建的工厂子类一处就行。减少了修改量。

    优点:克服了简单工厂所违背的开闭原则的缺点,又保持了封装对象创建过程的优点。
    缺点:开发量增大。


    1.3抽象工厂

    工厂方法模式针对的是一个产品等级结构(即所有产品实现同一个接口),而抽象工厂模式针对的是多个产品等级结构。

    抽象工厂模式中还有一个产品族的概念,产品族就是位于不同产品等级结构中的一系列相关的产品。是不是很拗口,举个栗子(看下面的UML图,图为抽象工厂的例子的UML),下图中的IUserDao接口的两个实现类OracleUserDAoImpl和MongoUserDaoImpl就是一个产品等级。OracleUserDAoImpl和OracleUserDAoImpl则属于一个产品族(他们都是oracle的操作类)。而抽象工厂的工厂接口对应的每一个实现类用来生产一个产品族的所有产品。图中的OracleFactoryImpl工厂就可以生产Oracle产品族的所有产品,如果某天系统更换数据库的话,直接把工厂换为Mongo的工厂就行,不用修改具体的userDao的实现(用到了多态)。

    本例参考自《大话设计模式》
    抽象工厂UML图

    具体代码如下,先创建两个产品等级的接口IUserDao和ITitleDao,在创建工厂的抽象,接下来去实现这三个接口。重点关注一下实现工厂接口的时候,每个具体的工厂实现类中生产的产品族的产品与产品等级之间的联系。

    
    /**
     * 用户接口(对应一个产品等级)
     * @author live
     *
     */
    public interface IUserDao{
    	void insert();
    }
    /**
     * 头衔接口(对应一个产品等级)
     * @author live
     *
     */
    public interface ITitleDao{
    	void create();
    }
    /**
     * 每个工厂实现可以生产一个产品族的所有产品
     */
    public interface IFactory{
    	/**
    	 * 获取UserDao
    	 * @return
    	 */
    	IUserDao createUser();
    	/**
    	 * 获取TitleDao
    	 * @return
    	 */
    	ITitleDao createTitle();
    }
    /**
     * 实现工厂接口--Oracle
     * @author live
     *
     */
    public class OracleFactoryImpl implements IFactory{
    	@Override
    	public IUserDao createUser() {
    		return new OracleUserDaoImpl();
    	}
    	@Override
    	public ITitleDao createTitle() {
    		return new OracleTitleDaoImpl();
    	}
    	
    }
    /**
     * 实现工厂接口--MongoDB
     * @author live
     *
     */
    public class MongoFactoryImpl implements IFactory{
    	@Override
    	public IUserDao createUser() {
    		return new MongoUserDaoImpl();
    	}
    	@Override
    	public ITitleDao createTitle() {
    		return new MongoTitleDaoImpl();
    	}
    	
    }
    
    --------------------具体的产品实现-------------------------------------
    /**
     * 用户操作--Oracle
     * @author live
     *
     */
    public class OracleUserDaoImpl implements IUserDao{
    	@Override
    	public void insert() {
    		System.out.println("在Oracle中添加了一个新用户");
    	}
    }
    /**
     * 用户操作--MongoDB
     * @author live
     *
     */
    public class MongoUserDaoImpl implements IUserDao{
    	@Override
    	public void insert() {
    		System.out.println("在MongoDB中添加了一个新用户");
    	}
    }
    /**
     * 头衔操作类--Oracle
     * @author live
     *
     */
    public class OracleTitleDaoImpl implements ITitleDao{
    	@Override
    	public void create() {
    		System.out.println("用户在Oracle中添加了一个头衔");
    	}
    	
    }
    /**
     * 头衔操作类--MongoDB
     * @author live
     *
     */
    public class MongoTitleDaoImpl implements ITitleDao{
    	@Override
    	public void create() {
    		System.out.println("用户在MongoDB中添加了一个头衔");
    	}
    	
    }
    /**
     * 抽象工厂模式
     * @author live
     *
     */
    public class AbstractFactory {
    	public static void main(String[] args) {
    		// 创建工厂
    		/**
    		 * 换数据库的时候只需要修改创建的工厂就行,不需要重新修改每一项
    		 */
    //		IFactory factory = new OracleFactoryImpl();
    		IFactory factory = new MongoFactoryImpl();
    		// 用户操作类
    		IUserDao userDao = factory.createUser();
    		// 头衔操作类
    		ITitleDao titleDao = factory.createTitle();
    		
    		userDao.insert();
    		titleDao.create();
    	}
    }
    
    

    抽象工厂适用于一个继承体系中,存在着多个等级结构(即存在着多个抽象类),并且分属各个等级结构中的实现类之间存在着一定的关联或者约束的情况。

    缺点:每次添加一个新的产品的时候都要修改所有工厂,所以对于抽象工厂来说产品的等级结构划分很重要。

    1.4多方法工厂(参考自其他人的博客)

    多方法工厂与简单工厂模式类似,只是修改了工厂的实现。将简单工厂的静态方法去掉,在工厂内添加多个静态方法,每一个静态方法对应一个产品,每次新增加产品的时候只需要在工厂中添加新的静态方法即可。避免修改具体方法的逻辑。

    工厂修改的代码如下。

    /**
     * 多方法工厂
     */
    public class SimpleFactory {
    	// 生产A
    	public static IPrinter createPrinterA(){
    		return new Printer_AImpl("小明","2018");
    	}
    	/**
    	 * 生产B
    	 * @return
    	 */
    	public static IPrinter createPrinterB(){
    		return new Printer_BImpl();
    	}
    }
    

    2.参考文章与书籍

    设计模式(一) 工厂模式 五种写法总结
    《大话设计模式》

    简书发布地址
    csdn发布地址
    如有错误,请多多指教

  • 相关阅读:
    科大奥瑞大物实验-A类不确定度计算器 代码
    在手机和电脑间双向加密传输文件 —— Windows 安装 Kde Connect
    Leetcode 寻找两个有序数组的中位数
    树莓派3B安装 Arch Linux ARM
    从零开始编译安装 Extcalc
    Oracle, PostgreSQl, MySQL针对INSERT时可能唯一键重复的SQL
    如何从Oracle, MySql, PostgreSQL的PreparedStatement获得所执行的sql语句?
    PostgreSQL报错:当前事务被终止,命令被忽略,直到事务块结束
    PostgreSQL对GROUP BY子句使用常量的特殊限制
    一种用JDBC实现批量查询的巧妙方法
  • 原文地址:https://www.cnblogs.com/liveinmyheart/p/9511344.html
Copyright © 2011-2022 走看看