设计模式-工厂模式(简单工厂、工厂方法、抽象工厂)(java代码案例)以及JDK类库中的工厂模式

简单工厂模式

定义

简单工厂模式又称静态工厂方法模式,它是由一个工厂角色统一创建不同的对象,但这些对象都继承自同一个类。

结构

1.工厂角色:根据传入不同的参数,实例化对应的具体类。

2.具体角色:工厂类返回的对象,它们继承自同一个父类。

3.父类:它是所有具体角色的父类,定义了所有公共接口

类图

设计模式-工厂模式(简单工厂、工厂方法、抽象工厂)(java代码案例)以及JDK类库中的工厂模式_第1张图片

案例

以实现一个计算器接口为例。

父类

package com.headfirst.simplefactorypattern.dao;

public class Calculator {

	double numA = 0;
	
	double numB = 0;
	
	public double getResult(){
		return 0.0;
	}
	
}

工厂类

package com.headfirst.simplefactorypattern.dao;

public class CalculatorFactory {
	
	public static Calculator cal = null;
	
	public static Calculator createFactory(String operate){
		switch (operate) {
		case "+":
			if(cal == null){
				cal = new plus();
			}
			break;
		case "-":
			if(cal == null){
				cal = new sub();
			}
			break;
		case "*":
			if(cal == null){
				cal = new mul();
			}
			break;
		case "/":
			if(cal == null){
				cal = new div();
			}
			break;
		}
		return cal;
	}

}

具体实现类

加法

package com.headfirst.simplefactorypattern.dao;

public class plus extends Calculator{

	public double getResult(){
		return numA + numB;
	}
	
}

减法

package com.headfirst.simplefactorypattern.dao;

public class sub extends Calculator{
	
	public double getResult(){
		return numA - numB;
	}
	
}

乘法

package com.headfirst.simplefactorypattern.dao;

public class mul extends Calculator{

	public double getResult(){
		return numA * numB;
	}
	
}

除法

package com.headfirst.simplefactorypattern.dao;

public class div extends Calculator{
	
	public double getResult(){
		return numA / numB;
	}
	
}

测试类

	public static void main(String[] args) {
		Calculator factory = CalculatorFactory.createFactory("/");
		factory.numA = 26;
		factory.numB = 2.5;
		System.out.println(factory.getResult());
	}

优点

1.实现使用和创建的分离

2.客户端不需要知道具体实现类的类名,只需要知道对应的参数即可创建对象

缺点

1.当需要新增一个具体类时,需要修改工厂类的代码逻辑,违背了OCP(开放-封闭原则)

2.由于工厂类中创建对象的方法是静态的,无法被继承实现

 

 

工厂方法模式

定义

工厂方法模式定义了一个用于创建对象的接口,让子类决定实例化那一个类,工厂方法是一个类的实例化延迟到其子类。

类图

设计模式-工厂模式(简单工厂、工厂方法、抽象工厂)(java代码案例)以及JDK类库中的工厂模式_第2张图片

工厂方法模式解决的问题

由于简单工厂模式违背了OCP开放-封闭原则,对修改封闭,所以在这里简单工厂的switch-case逻辑判断不存在了,每个具体实现类对应一个子类工厂,这些子类工厂都继承自抽象工厂接口。

当需要新增一个功能,只需要新增一个具体实现类和一个子类工厂即可。

案例

我还以实现一个计算器程序为例,这里省去具体实现类的代码。

工厂类接口

package com.headfirst.factorymethodmode.interfaces;

import com.headfirst.simplefactorypattern.dao.Calculator;

public interface ICalculatorFactory {

	public Calculator createFactory();
	
}

加法工厂类

package com.headfirst.factorymethodmode.dao;

import com.headfirst.factorymethodmode.interfaces.ICalculatorFactory;
import com.headfirst.simplefactorypattern.dao.Calculator;
import com.headfirst.simplefactorypattern.dao.plus;

public class addFactory implements ICalculatorFactory{

	@Override
	public Calculator createFactory() {
		Calculator cal = new plus();
		return cal;
	}

}

减法工厂类

package com.headfirst.factorymethodmode.dao;

import com.headfirst.factorymethodmode.interfaces.ICalculatorFactory;
import com.headfirst.simplefactorypattern.dao.Calculator;
import com.headfirst.simplefactorypattern.dao.sub;

public class subFactory implements ICalculatorFactory{

	@Override
	public Calculator createFactory() {
		Calculator cal = new sub();
		return cal;
	}

}

乘法工厂类

package com.headfirst.factorymethodmode.dao;

import com.headfirst.factorymethodmode.interfaces.ICalculatorFactory;
import com.headfirst.simplefactorypattern.dao.Calculator;
import com.headfirst.simplefactorypattern.dao.mul;

public class mulFactory implements ICalculatorFactory{

	@Override
	public Calculator createFactory() {
		Calculator cal = new mul();
		return cal;
	}

}

除法工厂类

package com.headfirst.factorymethodmode.dao;

import com.headfirst.factorymethodmode.interfaces.ICalculatorFactory;
import com.headfirst.simplefactorypattern.dao.Calculator;
import com.headfirst.simplefactorypattern.dao.div;

public class divFactory implements ICalculatorFactory{

	@Override
	public Calculator createFactory() {
		Calculator cal = new div();
		return cal;
	}

}

测试类

	public static void main(String[] args) {
		divFactory factory = new divFactory();
		Calculator cal = factory.createFactory();
		cal.setNumA(2.5);
		cal.setNumB(5.0);
		System.out.println(cal.getResult());
	}

优点

实现使用和创建的分离。

缺点

每增加一个具体实现类就要对应增加一个子类工厂,增加了工作量。

JDK类库中的工厂方法模式

java.lang.reflect.Proxy#newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)

Proxy:抽象工厂,它定义了一个创建对象的接口newProxyInstance。

在这个方法参数里面并没有显示地告诉我们具体工厂类是谁,但是仔细阅读源码会发现:通过ClassLoader和interfaces反射获取到了具体类的Clazz,这样我们就拿到了这个类地构造器方法,Constructor再调用newInstance方法即可得到具体类,在这里面具体工厂类就是通过反射得到的构造器!通过不同的构造器实例化不同的类。

@CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        if (h == null) {
            throw new NullPointerException();
        }

        final Class[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            final Constructor cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
                // create proxy instance with doPrivilege as the proxy class may
                // implement non-public interfaces that requires a special permission
                return AccessController.doPrivileged(new PrivilegedAction() {
                    public Object run() {
                        return newInstance(cons, ih);
                    }
                });
            } else {
                return newInstance(cons, ih);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        }
    } 
  

java.lang.reflect.Constructor#newInstance()

java.lang.Class#newInstance()

lang包下的Class和reflect中的Constructor都是工厂方法模式中的抽象工厂,通过反射获得的构造器是具体工厂,原理可参考Proxy来理解即可。

 

抽象工厂模式

定义

抽象工厂模式是提供创建一系列相关或相互依赖对象的接口,而无需指定具体的类。

结构

1.抽象工厂接口(abstractfactory):相当于一个超级工厂,它是工厂的工厂,它包含了所有产品创建的方法,在这里是抽象方法。

2.具体工厂(concretefactory):创建具有特定实现的产品对象,多个具体工厂跟抽象产品的具体实现类对应。

3.抽象产品(abstractproduct):定义了该产品的一系列接口,多个抽象产品的方法都汇总在抽象工厂接口中。

4.具体实现类(product):一个具体实现类对应一个具体工厂,一个具体工厂可以对应多个具体实现类。

类图

设计模式-工厂模式(简单工厂、工厂方法、抽象工厂)(java代码案例)以及JDK类库中的工厂模式_第3张图片

案例

以读写User对象和department对象需要同时实现oracle数据库读写和mysql数据库读写为例(user类和department类的代码这里没有贴出来)

产品A:IUser(接口)

package com.headfirst.abstractfactorymode.interfaces;

import com.headfirst.abstractfactorymode.dao.User;

public interface IUser {

	void insertUser(User user);
	
	User getUser();
	
	
}

 产品B:IDepartment(接口)

package com.headfirst.abstractfactorymode.interfaces;

import com.headfirst.abstractfactorymode.dao.Department;

public interface IDepartment {

	void insertDepartment(Department department);
	
	Department getDepartment();
	
}

oracle读写user对象的具体实现类:OracleUser

package com.headfirst.abstractfactorymode.dao;

import com.headfirst.abstractfactorymode.interfaces.IUser;

public class OracleUser implements IUser {

	@Override
	public void insertUser(User user) {
		System.out.println("向oracle中插入一条User对象");
	}

	@Override
	public User getUser() {
		System.out.println("从oracle中查询User对象");
		return null;
	}

}

oracle读写department对象的具体实现类:OracleDepartment

package com.headfirst.abstractfactorymode.dao;

import com.headfirst.abstractfactorymode.interfaces.IDepartment;

public class OracleDepartment implements IDepartment {

	@Override
	public void insertDepartment(Department department) {
		System.out.println("向oracle中插入一条department对象");
	}

	@Override
	public Department getDepartment() {
		System.out.println("从oracle中查询department对象");
		return null;
	}

}

mysql读写user对象的具体实现类:MysqlUser

package com.headfirst.abstractfactorymode.dao;

import com.headfirst.abstractfactorymode.interfaces.IUser;

public class MysqlUser implements IUser {

	@Override
	public void insertUser(User user) {
		System.out.println("向mysql中插入一条User对象");
	}

	@Override
	public User getUser() {
		System.out.println("从mysql中查询User对象");
		return null;
	}

}

mysql读写department对象的具体实现类:MySQLDepartment

package com.headfirst.abstractfactorymode.dao;

import com.headfirst.abstractfactorymode.interfaces.IDepartment;

public class MySQLDepartment implements IDepartment {

	@Override
	public void insertDepartment(Department department) {
		System.out.println("向mysql中插入一条department对象");
	}

	@Override
	public Department getDepartment() {
		System.out.println("从mysql中查询department对象");
		return null;
	}

}

抽象工厂接口:IFactory

package com.headfirst.abstractfactorymode.interfaces;

import com.headfirst.abstractfactorymode.dao.Department;
import com.headfirst.abstractfactorymode.dao.User;

public interface IFactory {

	IUser createUser();
	
	IDepartment createideDepartment();
	
}

Oracle读写对象的具体工厂:OracleFactory

package com.headfirst.abstractfactorymode.dao;

import com.headfirst.abstractfactorymode.interfaces.IDepartment;
import com.headfirst.abstractfactorymode.interfaces.IFactory;
import com.headfirst.abstractfactorymode.interfaces.IUser;

public class OracleFactory implements IFactory {

	@Override
	public IUser createUser() {
		return new OracleUser();
	}

	@Override
	public IDepartment createideDepartment() {
		return new OracleDepartment();
	}

}

mysql读写对象的具体工厂:MysqlFactory

package com.headfirst.abstractfactorymode.dao;

import com.headfirst.abstractfactorymode.interfaces.IDepartment;
import com.headfirst.abstractfactorymode.interfaces.IFactory;
import com.headfirst.abstractfactorymode.interfaces.IUser;

public class MysqlFactory implements IFactory {

	@Override
	public IUser createUser() {
		return new MysqlUser();
	}

	@Override
	public IDepartment createideDepartment() {
		return new MySQLDepartment();
	}
	
}

测试类:

	public static void main(String[] args) {
		OracleFactory oracleFactory = new OracleFactory();
		IUser iUser = oracleFactory.createUser();
		iUser.insertUser(new User());
		iUser.getUser();
		IDepartment iDepartment = oracleFactory.createideDepartment();
		iDepartment.insertDepartment(new Department());
		iDepartment.getDepartment();
		MysqlFactory mysqlFactory = new MysqlFactory();
		IUser iUser2 = mysqlFactory.createUser();
		iUser2.insertUser(new User());
		iUser2.getUser();
		IDepartment iDepartment2 = mysqlFactory.createideDepartment();
		iDepartment2.insertDepartment(new Department());
		iDepartment2.getDepartment();
	}

测试结果:

设计模式-工厂模式(简单工厂、工厂方法、抽象工厂)(java代码案例)以及JDK类库中的工厂模式_第4张图片

优点

灵活地交换产品,保证产品族中使用同一个产品,例如数据库要么全部使用oracle,要么全部使用mysql

缺点

当增加一个新的产品时,维护起来很困难,例如新加一个sqlserver,需要新增一套支持sqlserver的具体实现类和具体工厂。

 

 

反射(配置文件)+抽象工厂实现不同数据库程序

由于抽象工厂模式要求一个具体实现类对应一个具体工厂类,所以在增加新功能时,需要重新写一个具体实现类,一个具体工程类并在抽象工厂类中增加新的实例化方法,这样写无疑增加了工作量,所以这里在抽象工厂模式基础上用反射技术来解决这个问,一起往下看。

类图

设计模式-工厂模式(简单工厂、工厂方法、抽象工厂)(java代码案例)以及JDK类库中的工厂模式_第5张图片

案例

这里省去了抽象产品接口和具体实现类的代码

DataAccess类

package com.headfirst.abstractfactorymode.dao;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.InputStream;
import java.util.Properties;
import java.util.ResourceBundle;

import com.headfirst.abstractfactorymode.interfaces.IDepartment;
import com.headfirst.abstractfactorymode.interfaces.IUser;

public class DataAccess {

	private static String db = "";
	
	static{
		try {
			//从db.properties中读取配置信息
			Properties properties = new Properties();
			InputStream inputStream = DataAccess.class.getClassLoader()
			.getResourceAsStream("com/headfirst/abstractfactorymode/config/db.properties");
			properties.load(inputStream);
			db = properties.getProperty("db.dialect");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static IUser createUser() {
		String className = "com.headfirst.abstractfactorymode.dao."+ db +"User";
		try {
			return (IUser) Class.forName(className).newInstance();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public static IDepartment createDepartment() {
		String className = "com.headfirst.abstractfactorymode.dao."+ db +"Department";
		try {
			return (IDepartment) Class.forName(className).newInstance();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

}

db.properties

db.dialect=Oracle

测试类

	public static void main(String[] args) throws Exception {
		IUser iUser = DataAccess.createUser();
		iUser.insertUser(new User());
		iUser.getUser();
		IDepartment iDepartment = DataAccess.createDepartment();
		iDepartment.insertDepartment(new Department());
		iDepartment.getDepartment();
	}

这里用简单工厂模式将一个DataAccess类代替了抽象工厂类和具体实现类,将createUser和createDepartment方法写在DataAccess类中,由于我们前面总结过:简单工厂模式的缺点是当增加新的一个功能时,需要在switch中增加case条件,违背了OCP开放-封闭原则。所以这里动态获取db.properties文件中的db.dialect,再通过反射技术实例化不同数据库操作对象,如果2加客户,一家用的mysql,一家用的oracle,只需要修改db.properties。

JDK类库中的抽象工厂模式

java.util.Calendar#getInstance()

Calendar类的getInstance方法返回一个抽象类或者接口,并且通过传入不同类型的标识符来实例化不同的对象。

    /**
     * Gets a calendar using the default time zone and locale. The
     * Calendar returned is based on the current time
     * in the default time zone with the default locale.
     *
     * @return a Calendar.
     */
    public static Calendar getInstance()
    {
        Calendar cal = createCalendar(TimeZone.getDefaultRef(), Locale.getDefault(Locale.Category.FORMAT));
        cal.sharedZone = true;
        return cal;
    }
    private static Calendar createCalendar(TimeZone zone,
                                           Locale aLocale)
    {
        Calendar cal = null;

        String caltype = aLocale.getUnicodeLocaleType("ca");
        if (caltype == null) {
            // Calendar type is not specified.
            // If the specified locale is a Thai locale,
            // returns a BuddhistCalendar instance.
            if ("th".equals(aLocale.getLanguage())
                    && ("TH".equals(aLocale.getCountry()))) {
                cal = new BuddhistCalendar(zone, aLocale);
            } else {
                cal = new GregorianCalendar(zone, aLocale);
            }
        } else if (caltype.equals("japanese")) {
            cal = new JapaneseImperialCalendar(zone, aLocale);
        } else if (caltype.equals("buddhist")) {
            cal = new BuddhistCalendar(zone, aLocale);
        } else {
            // Unsupported calendar type.
            // Use Gregorian calendar as a fallback.
            cal = new GregorianCalendar(zone, aLocale);
        }

        return cal;
    }

java.sql.DriverManager#getConnection(String user,String password)

通过加载不同的数据库驱动,可以获取到不同的数据库连接,驱动类将连接注册到一个map中,当DriverManager调用getConnection(String url)方法可以根据参数url获取到不同的连接,如果加载的oracle驱动,则获得一个OracleConnection的链接,如果加载的是mysql驱动,则会获取到一个MultiHostMySQLConnection的链接。有兴趣的话可以自己研究一下驱动包的源码。

java.util.ResourceBundle#getBundle(String basename)

ResourceBundle有2个子类:

①PropertyResourceBundle,读取properties配置文件信息。

用法:

		/*
		 *   其中new Locale(“zh”, “CN”)提供本地化信息,上面这行代码,程序会首先在classpath下寻找
		 *   my_zh_CN.properties文件,若my_zh_CN.properties文件不存在,则取找my_zh.properties,
		 *   如还是不存在,继续寻找my.properties,若都找不到就抛出异常。
		 */
		ResourceBundle bundle = ResourceBundle.getBundle("my",new Locale("zh", "CN"));
		String str = (String) bundle.getObject("key");
		System.out.println(str);

②ListResourceBundle,直接读取key/value值。

你可能感兴趣的:(设计模式,设计模式轻松学)