(一)简单工厂模式
简单工厂模式又称静态工厂方法模式。它存在的目的很简单:定义一个用于创建对象的接口。
组成结构:
1) 工厂类角色(Creator):这是本模式的核心,含有一定的商业逻辑和判断逻辑。在java中它往往由一个具体类实现。
2) 抽象产品角色(Product):它一般是具体产品继承的父类或者实现的接口。在java中由接口或者抽象类来实现。
3) 具体产品角色(ConcreteProduct):工厂类所创建的对象就是此角色的实例。在java中由一个具体类实现。
用类图表示它们之间的关系:
代码示例:
抽象产品角色 -- 图形:
public interface Figure(){public double calculateArea();//计算面积}
具体产品角色 -- 正方形、圆形:
public class Square implements Figure{//正方形
public Square(){}
public Square(double length){
this.length = length;
}
private double length;
// getters/setters ...
@Override
public double calculateArea(){
return this.getLength() * this.getLength();
}
}
public class Circle implements Figure{//圆形
public Circle(){}
public Circle(double radius){
this.radius = radius;
}
private double radius;
// getters/setters ...
@Override
public double calculateArea(){
return this.getRadius() * this.getRadius() * Math.PI;
}
}
工厂类角色-图形工厂:
public class FigureFactory{
public static Figure createFigure(String typeName, double figureArgs){
if("Circle".equalsIgnoreCase(typeName)){
return new Circle(figureArgs);
}else if("Square".equalsIgnoreCase(typeName)){
return new Square(figureArgs);
}
}
}
//测试代码
public class Test {
public static void main(String []args){
Figure figure = null;
if(...){//界面逻辑需要圆形
figure = FigureFactory.createFigure("Circle", 2.0);
}else if(...){//界面逻辑需要正方形
figure = FigureFactory.createFigure("Square", 2.0);
}
System.out.println("面积为:"+figure.calculateArea());
}
}
(二)工厂方法模式
工厂方法模式去掉了简单工厂模式中工厂方法的静态属性,使得它可以被子类继承。这样在简单工厂模式里集中在工厂方法上的压力可以由工厂方法模式里不同的工厂子类来分担。
组成结构:
1) 抽象工厂角色(Creator): 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在 java 中它由抽象类或者接口来实现。
2) 具体工厂角色(ConcreteCreator1,2):它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。
3) 抽象产品角色(Product):它是具体产品继承的父类或者是实现的接口。在 java 中一般有抽象类或者接口来实现。
4) 具体产品角色(ConcreteProduct1,2):具体工厂角色所创建的对象就是此角色的实例。在 java 中由具体的类来实现。
用类图表示它们之间的关系:
示例代码:
抽象产品角色,具体产品角色与简单工厂模式类似
...
抽象工厂 -- 图形工厂,与具体工厂 -- 圆形工厂,正方形工厂:
//图形工厂
public interface FigureFactory{
public Figure createFigure(double figureArgs);
}
//圆形工厂
public class CircleFactory implements FigureFactory{
@Override
public Figure createFigure(double figureArgs){
return new Circle(figureArgs);
}
}
//正方形工厂 与 圆形工厂类似,此处省略
//测试代码
public class Test {
public static void main(String []args){
FigureFactory figureFactory = null;
if(...){//界面逻辑需要圆形
figureFactory = new CircleFactory();
}else if(...){//界面逻辑需要正方形
figureFactory = new SqureFactory();
}
Figure figure = figureFactory.createFigure(2.0);
System.out.println("面积为:"+figure.calculateArea());
}
}
当客户端不需要了解具体产品的创建细节,可以使用工厂方法模式。
(三)抽象工厂模式
抽象工厂模式和工厂方法模式的区别就在于需要创建对象的复杂程度上。
而且抽象工厂模式是三个里面最为抽象、最具一般性的。
抽象工厂模式的用意为:给客户端提供一个接口,可以创建多个产品族中的产品对象而且使用抽象工厂模式还要满足以下条件:
1)系统中有多个产品族,而系统一次只可能消费其中一族产品。
组成结构:
与工厂方法模式一致,这里省略。
类图:
为了方便理解,增加一个具体共产:颜色工厂。
代码示例:
抽象产品 -- 颜色:
public interface Color{//颜色
//上色
public void fill();
}
具体产品 -- 黄色,红色:
public class Yellow implements Color{//黄色
private String color;
// getters/setters ...
@Override
public void fill(){
this.setColor("yellow");
}
}
public class Red implements Color{//红色
private String color;
// getters/setters ...
@Override
public void fill(){
this.setColor("red");
}
}
抽象工厂:
public interface ColorFigureFactory{
public Color createColor(String color);
public Figure createFigure(double figureArgs);
}
具体工厂 -- 颜色工厂,图形工厂:
//颜色工厂
public class ColorFactory implements ColorFigureFactory{
public Color createColor(String color){
return ...
}
public Figure createFigure(String typeName, double figureArgs);
}
//图形工厂
public class FigureFactory implements ColorFigureFactory{
public Color createColor(String color){
return ...
}
public Figure createFigure(String typeName, double figureArgs){
return ...
}
}
3.1)抽象工厂模式在实际开发中的应用
实现功能:在不同数据库下创建用户,部门;
抽象产品 -- 用户(IUserDao),部门(IDepartmentDao)
//用户数据交互层
public interface IUserDao {
public void insert(User user);
public User get(int id);
}
//部门数据交互层
public interface IDepartmentDao {
public void insert(Department dept);
public Departmentget(int id);
}
具体产品 -- OracleUserDao,OracleDepartmentDao,PostgreUserDao,PostgreDepartmentDao
//用户Dao
public class OracleUserDao implements IUserDao{
@Override
public void insert(User user) {
System.out.println("在oracle中的user表中插入一条元素");
}
@Override
public User get(int id) {
System.out.println("在oracle中的user表得到id为"+id+"的一条数据");
return null;
}
}
public class PostgreUserDao implements IUserDao{
@Override
public void insert(User user) {
System.out.println("在postgre中的user表中插入一条元素");
}
@Override
public User get(int id) {
System.out.println("在postgre中的user表得到id为"+id+"的一条数据");
return null;
}
}
//部门Dao类似,略
抽象工厂 -- IFactory
public interface IFactory {
public IUserDao createUserDao();//用于访问User表的对象
public IDepartmentDao createDepartmentDao();//用于访问Department表的对象
}
具体工厂 -- Oracle工厂,Postgre工厂:
//Oracle工厂
public class OracleFactory implements IFactory {
@Override
public IUserDao createUserDao() {
return new OracleUserDao();
}
@Override
public IDepartmentDao createDepartmentDao() {
return new OracleDepartmentDao();
}
}
//PG工厂
public class PostgreFactory implements IFactory {
@Override
public IUserDao createUserDao() {
return new PostgreUserDao();
}
@Override
public IDepartmentDao createDepartmentDao() {
return new PostgreDepartmentDao();
}
}
测试代码:
public void test(){
IFactory factory = new PostgreFactory();
IUserDao userDao = factory.createUserDao();
userDao.insert(new User("Michael"));
}
总结:
抽象工厂模式,下的工厂是可以生产产品系列的。工厂方法模式只能生产单一产品的。
优点:
抽象工厂模式最大的好处是易于交换产品系列,由于具体工厂类,例如 IFactory factory=new OracleFactory();
在一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易,它只需要改变具体工厂即可使用不同的产品配置。不管是任何人的设计都无法去完全防止需求的更改,或者项目的维护,那么我们的理想便是让改动变得最小、最容易,例如我现在要更改以上代码的数据库访问时,只需要更改具体的工厂即可。抽象工厂模式的另一个好处就是它让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操作实例,产品实现类的具体类名也被具体的工厂实现类分离,不会出现在客户端代码中。就像我们上面的例子,客户端只认识IUser和IDepartment,至于它是PostgreSQl里的表还是Oracle里的表就不知道了。 缺点:
如果你的需求来自增加功能,比如增加Department表,就有点太繁琐了。首先需要增加 IDepartment,OracleDepartment, PostgreDepartment。 然后我们还要去修改工厂类:
IFactory, OracleFactory, PostgreFactory 才可以实现,需要修改三个类,比较麻烦。客户端程序肯定不止一个,每次都需要声明IFactory factory=new OracleFactory(), 如果有100个调用数据库的类,就需要更改100次。
3.1.1)改进
改进思路:
①将抽象工厂及其子类修改为一个静态工厂,解决每次都要new factory才能使用dao的繁琐之处,其次,新增一个比如机构表(Organization)不用再去修改IFactory, OracleFactory,PostgreFactory三个工厂类,只要修改这一个静态工厂即可;
②通过在配置文件指定数据库的类型,就可以达到修改配置文件即可切换数据库的效果。
③通过反射,直接根据变量返回对应变量名的实例,精简了诸多代码(根据条件判断,new 某一个Dao)
public class newFactory {
private static String packageName = "com.test";
@Value("${db.type:Oracle}")
private static String sqlName;//Oracle Or Postgre Or ...
public static IUserDto createUserDto() throws Exception{
String className = packageName + "." + sqlName + "UserDto";
return (IUserDto)Class.forName(className).newInstance();
}
public static IDepartmentDto createDepartmentDto() throws Exception{
String className = packageName + "." + sqlName + "DepartmentDto";
return (IDepartmentDto)Class.forName(className).newInstance();
}
}