设计模式之工厂模式(二)

工厂模式

工厂模式目的是让对象的创建更加的灵活和易于维护.实现了创建者和调用者的分离

原则是:客户类只使用服务,而不承担维护的责任

1   什么时候需要工厂模式?

1.创建对象的过程比较复杂, 需要其他类的辅助, 需要大量计算,需要大量配置信息来获取等(比如connection).需要工厂模式来支持

2.当对象的实例有可能发生改变的时候

 

2   工厂模式的分类:

  1. 简单工厂:

用来创建同一等级(同一个接口的不同实现类)结构中的任意产品

弊端:对于增加新的产品,需要修改工厂中的逻辑

  1. 工厂方法:

弥补了简单工厂模式的缺点(不完全满足开闭原则)

工厂方法和简单工厂最大的不同在于,简单工厂只有一个工厂类,而工厂方法有一组实现了相同接口的工厂类

抽象工厂

  1. 抽象工厂:

特点:用来生产不同产品族的全部产品(对于增加新的产品,无能为力;支持增加产品族)

产品族:一系列有特定关系的产品.比如汽车工厂的发动机,轮胎和座椅

  1. 简单工厂和工厂方法的比较:
  1. 结构复杂度:

简单工厂在结构复杂度上比较有优势,只需要一个工厂类就可以,而工厂方法随着产品的增加,工厂也会增加,类结构将会越来越复杂

  1. 代码复杂度:

代码复杂度和结构复杂度是互斥的,简单工厂正在结构方面相对简洁,但是在代码复杂度上比工厂模式复杂.简单工厂随着产品增多,工厂类的逻辑也越来越臃肿,而工厂方法中每个工厂只负责实例化一个产品,职责单一,代码简洁

  1. 管理上的难度:

从扩展性上来说,工厂方法完全满足OCP(开闭原则),即具有良好的扩展性,那是否就说明了简单工厂没有扩展性呢,答案是否定的,简单工厂同样也具有良好的扩展性,只不过扩展的时候需要修改工厂类少量的代码,尽管没有完全满足OCP,但是如果能够在合理范围内,做一点妥协,牺牲一点原则,也是可以的,具体采用哪种设计模式,需要看对应的业务场景                                                                                                                                                           

 

工厂模式的要点总结:

简单工厂(静态工厂):

某种程度来说,虽然不完全符合设计原则,但是项目中使用比较多

工厂方法:

符合设计开闭原则,能在不修改已有类的前提下,通过增加新的工厂来实现扩展

抽象工厂:

不可以增加产品,可以增加产品族(比如新增一条中配汽车的生产线:需要中端发动机,中端座椅和轮胎);

 

1.静态工厂, 简单工厂

解决对象的实例有可能将来要发生改变的情况

前提:
    面向接口编程
    
1.定义一个接口
    UserDao
        UserDaoForMysql   访问mysql的实现类
        UserDaoForOracle    访问oracle的实现类
    通过传入的参数决定要返回的实现类
2.定义一个工厂
    工厂中定义一个静态的get方法, 是为了返回一个UserDao类型的对象
    
3.在调用者中通过工厂的get方法来获取对象

public class UserDaoFactory {
	public static UserDao getUserDao() {
		//改造参数, 以前是由外部通过参数传递进来, 决定实例化mysql 或者 oracle,改为通过读取配置文件, 手动获取
		String type = null;
		Properties properties = new Properties();
		try {
			//读取配置文件,修改时不需要修改java代码,直接修改配置文件dbType.properties
			properties.load(new FileInputStream("src/dbType.properties"));
			
			type = properties.getProperty("dbType");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		UserDao userDao = null;
		//根据type类型来决定返回mysql 或者 oracle 的实例
		if("mysql".equals(type)) {
			userDao = new UserDaoForMysql();
		}else if("oracle".equals(type)) {
			userDao = new UserDaoForOracle();
		}
		return userDao;
	}
}

service层调用userDao不需要指定调用UserDao的哪个实现类,在配置文件中就指定了调用的实现类,例如下面指定调用oracle

配置文件:

设计模式之工厂模式(二)

service:

public class UserServiceImpl implements UserService {
	
	//service完成业务逻辑, 需要依赖于userDao的一个实例
	//等号左边声明为接口类型
	private UserDao userDao = UserDaoFactory.getUserDao();
	@Override
	public void addUser() {
		userDao.addUser();

	}

}

结果:

设计模式之工厂模式(二)

2.工厂方法

1.定义产品的接口


    UserDao
    为产品添加不同的实现类
        UserDaoForMysqlImpl
        UserDaoForOracleImpl


    UserDao接口:

 

public interface UserDao {
	void addUser();
}

UserDaoForMysqlImpl
UserDaoForOracleImpl

public class UserDaoForMysqlImpl implements UserDao {
	@Override
	public void addUser() {
		System.out.println("向mysql数据库插入记录");
	}
}
public class UserDaoForOracleImpl implements UserDao {
	@Override
	public void addUser() {
		System.out.println("向oracle插入一条记录");
	}
}


2.定义工厂的接口


    UserDaoFactory >> getUserDao >> 返回一个UserDao接口的实现类(mysql/oracle)
    UserDaoFactory >> UserDaoFactoryForMysql/UserDaoFactoryForOracle
    两个实现类分别用于返回 (生产)UserDaoForMysqlImpl/UserDaoForOracleImpl实现类对象
    两个工厂分别用来生产两个产品(UserDao的实现类对象)

UserDaoFactory

public interface UserDaoFactory {
	UserDao getUserDao();
}

UserDaoFactoryForMysql/UserDaoFactoryForOracle

public class UserDaoFactoryForMysql implements UserDaoFactory {
	@Override
	public UserDao getUserDao() {
		return new UserDaoForMysqlImpl();
	}
}
public class UserDaoFactoryForOralce implements UserDaoFactory {
	@Override
	public UserDao getUserDao() {
		return new UserDaoForOracleImpl();
	}
}

   该接口定义的方法用于返回一个固定类型的对象(UserDao)
    为工厂定义实现类
    生产UserDaoForMysqlImpl的工厂

 

3.在客户端首先创建工厂, 然后再从工厂中获取对象

public class Client1 {
	
	public static void main(String[] args) {
		//通过工厂获取dao对象,获取mysql
		UserDao userDao1 = new UserDaoFactoryForMysql().getUserDao();
		userDao1.addUser();
        //获取类名
		System.out.println(userDao1.getClass().getSimpleName());
		System.out.println("============");
		//通过工厂获取dao对象,获取oracle
		UserDao userDao2 = new UserDaoFactoryForOralce().getUserDao();
		userDao2.addUser();
        //获取类名
		System.out.println(userDao2.getClass().getSimpleName());
	}
}

4.输出结果

设计模式之工厂模式(二)