简单工厂模式又称静态工厂方法模式,它是由一个工厂角色统一创建不同的对象,但这些对象都继承自同一个类。
1.工厂角色:根据传入不同的参数,实例化对应的具体类。
2.具体角色:工厂类返回的对象,它们继承自同一个父类。
3.父类:它是所有具体角色的父类,定义了所有公共接口
以实现一个计算器接口为例。
父类
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.由于工厂类中创建对象的方法是静态的,无法被继承实现
工厂方法模式定义了一个用于创建对象的接口,让子类决定实例化那一个类,工厂方法是一个类的实例化延迟到其子类。
由于简单工厂模式违背了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());
}
实现使用和创建的分离。
每增加一个具体实现类就要对应增加一个子类工厂,增加了工作量。
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
java.lang.reflect.Constructor#newInstance()
java.lang.Class#newInstance()
lang包下的Class和reflect中的Constructor都是工厂方法模式中的抽象工厂,通过反射获得的构造器是具体工厂,原理可参考Proxy来理解即可。
抽象工厂模式是提供创建一系列相关或相互依赖对象的接口,而无需指定具体的类。
1.抽象工厂接口(abstractfactory):相当于一个超级工厂,它是工厂的工厂,它包含了所有产品创建的方法,在这里是抽象方法。
2.具体工厂(concretefactory):创建具有特定实现的产品对象,多个具体工厂跟抽象产品的具体实现类对应。
3.抽象产品(abstractproduct):定义了该产品的一系列接口,多个抽象产品的方法都汇总在抽象工厂接口中。
4.具体实现类(product):一个具体实现类对应一个具体工厂,一个具体工厂可以对应多个具体实现类。
以读写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();
}
测试结果:
灵活地交换产品,保证产品族中使用同一个产品,例如数据库要么全部使用oracle,要么全部使用mysql
当增加一个新的产品时,维护起来很困难,例如新加一个sqlserver,需要新增一套支持sqlserver的具体实现类和具体工厂。
由于抽象工厂模式要求一个具体实现类对应一个具体工厂类,所以在增加新功能时,需要重新写一个具体实现类,一个具体工程类并在抽象工厂类中增加新的实例化方法,这样写无疑增加了工作量,所以这里在抽象工厂模式基础上用反射技术来解决这个问,一起往下看。
这里省去了抽象产品接口和具体实现类的代码
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。
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值。