当前位置: 首页 > news >正文

Java设计模式之工厂设计模式

简介

工厂模式是一种常见的设计模式,用于创建对象的过程中,通过工厂类来封装对象的创建过程。其核心思想是将对象的创建和使用分离,从而降低耦合度,提高代码的可维护性和可扩展性。工厂模式通常包括三种类型:简单工厂工厂方法抽象工厂

工厂模式与其他设计模式的主要区别在于,它是一种创建型模式,用于创建对象的过程中,通过工厂类来封装对象的创建过程。与之类似的还有单例模式、建造者模式等。工厂模式主要用于以下两个方面:

  1.     对象的创建和使用分离:将对象的创建过程封装到工厂类中,避免了客户端直接依赖具体的产品类,从而提高了代码的可维护性和可扩展性。
  2.     创建多个产品族或产品等级结构:当需要创建多个产品族或产品等级结构时,工厂模式可以提供一个统一的接口,方便客户端进行调用。
     

什么是简单工厂模式?

简单工厂模式也叫静态工厂模式,属于类的创建型模式。提供一个创建对象实例的功能,而无须关心其具体实现。被创建实例的类型可以是接口、抽象类,也可以是具体的类。 

简单工厂模式的本质:选择实现 。

设计意图: 通过专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。

简单工厂模式的结构

简单工厂模式涉及的角色及其职责如下:

  • 工厂(Factory)角色:简单工厂模式的核心,它负责实现创建所有实例的内部逻辑,该类可以被外界直接调用,创建所需的产品对象。
  • 抽象(Product)角色:简单工厂模式所创建的所有对象的父类,它负责约定所有具体产品类所共有的公共接口。
  • 具体产品(ConcreteProduct)角色:简单工厂模式所创建的真正实例对象。

以具体代码为例: 

现在有两种水果:苹果(Apple)和香蕉(Banana),为了演示,为这两种水果添加一个采集get()方法。 

public class Apple{/** 采集*/public void get(){System.out.println("采集苹果");}
}
public class Banana{/** 采集*/public void get(){System.out.println("采集香蕉");}
}

如果要采集水果,在不使用任何设计模式的情况下,我们一般都会这样做。

public class Client {public static void main(String[] args){//实例化一个AppleApple apple = newApple();//实例化一个BananaBanana banana = newBanana();apple.get();banana.get();}
}

客户端分别创建苹果和香蕉两个具体产品的实例并调用其采集的方法,这样做存在的问题是:客户端需要知道所有的具体水果种类,即客户端与所有的水果类都存在了耦合关系,不符合面向对象设计中的“低耦合”原则。

以上问题如何解决呢?答案:使用简单工厂,利用接口进行“封装隔离”。 

Apple和Banana类既然都是水果,且共有一个采集的get()方法,我们可以为其添加一个父类或让其实现一个共同的接口。在此,我们添加一个Fruit接口。  

public interface Fruit {/** 采集*/public void get();
}

让Apple和Banana类都实现Fruit接口。

public class Apple implements Fruit{/** 采集*/public void get(){System.out.println("采集苹果");}
}
public class Banana implements Fruit{/** 采集*/public void get(){System.out.println("采集香蕉");}
}

接下来创建一个水果工厂类FruitFactory,在工厂中实现对Apple和Banana类的实例化。

public class FruitFactory {/** 获得Apple类的实例*/public static  Fruit getApple(){return new Apple();}/** 获得Banana类实例*/public static Fruit getBanana(){return new Banana();}
}

若FruitFactory中getXXX()方法未声明为static的话,每次调用方法需创建FruitFactory实例比较麻烦,我们直接声明方法为static。

这样,我们就能够使用FruitFactory创建Apple和Banana实例对象了。 

public class Client{public static void main(String[] args){//实例化一个AppleFruit apple = FruitFactory.getApple();Fruit banana = FruitFactory.getBanana();apple.get();banana.get();}
}

观察以上工厂,其为每一个水果具体类都添加了一个getXXX()方法,当水果种类增多时方法数量也会增加导致FruitFactory类比较臃肿,而且各个方法之间关系也不够紧密不符合“高内聚”原则,我们再对其进行优化。 

public class FruitFactory {/** getFruit方法,获得所有产品对象*/public static Fruit getFruit(String type) throws InstantiationException, IllegalAccessException{if(type.equalsIgnoreCase("apple")) {return Apple.class.newInstance();} elseif(type.equalsIgnoreCase("banana")) {return Banana.class.newInstance();} else{System.out.println("找不到相应的实例化类");return null;}}
}

在客户端,使用改良后的FruitFactory来创建Apple和Banana实例对象。

public class Client{public static void main(String[] args){Fruit apple = FruitFactory.getFruit("apple");Fruit banana = FruitFactory.getFruit("banana");apple.get();banana.get();}
}

运行程序打印结果如下: 

采集苹果 
采集香蕉

什么是工厂方法模式?

工厂方法模式同样属于类的创建型模式又被称为多态工厂模式 。工厂方法模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责声明具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。 

模式中包含的角色及其职责

  • 抽象工厂(Factory)角色:工厂方法模式的核心,所有具体工厂类都必须实现这个接口。
  • 具体工厂( ConcreteFactory)角色:具体工厂类是抽象工厂的一个实现,负责实例化具体的产品。
  • 抽象产品(Product)角色:工厂方法模式所创建的所有具体产品的父类,或约定所有具体产品类都应实现的公共接口。
  • 具体产品(ConcreteProduct)角色:工厂方法模式所创建的真正对象。

在简单工厂模式中我们了解了简单工厂模式,我们使用如下具体的工厂FruitFactory对所有的水果对象进行实例化,缺点是显而易见的:当有新品种的水果需要产生时就需要修改工厂的getFruit()方法,加入新品种水果的逻辑判断和业务代码,这极不符合java“开放--封闭”原则。 

public class FruitFactory {/** get方法,获得所有产品对象*/public static Fruit getFruit(String type) throws InstantiationException, IllegalAccessException, ClassNotFoundException {if(type.equalsIgnoreCase("apple")) {return Apple.class.newInstance();} else if(type.equalsIgnoreCase("banana")) {return Banana.class.newInstance();} /** 如果有新品种的水果需要生产需在此处增加相应逻辑判断及业务代码,例如:* else if(type.equalsIgnoreCase("pear")) {*         return Pear.class.newInstance();*    } */else {System.out.println("找不到相应的实例化类");return null;}}
}

接下来我们使用工厂方法模式对以上工厂进行改进,首先工厂方法模式中FruitFactory被设计为了抽象工厂(可以是抽象类或者接口),它不再负责具体的产品创建,而是将产品的创建工作推迟到了由它的子类来实现,接下来以具体的代码为例。

抽象后的FruitFactory仅用来声明其所有子类需实现的接口。

public interface FruitFactory {public Fruit getFruit();
}

有了这个抽象工厂以后,假如我们需要苹果了该怎么办?很简单,创建一个生产苹果的工厂AppleFactory即可。

public class AppleFactory implements FruitFactory{public Fruit getFruit(){return new Apple();}
}

假如我们又需要获取香蕉了该怎么办?依旧简单,创建一个生产香蕉的工厂BananaFactory即可。

public class BananaFactory implements FruitFactory{public Fruit getFruit(){return new Banana();}
}

如果以后还有其他更多新品种的水果加入,只需要创建一个生产相应水果的工厂并让该工厂实现抽象工厂FruitFactory 的接口,需要哪种水果我们就调用相应工厂的getFruit()方法得到水果,这样做有一个好处,无论我们增加多少种水果,我们只需增加一个生产相应水果的工厂即可,无需对现有的代码进行修改,这就很好的符合了"开放--封闭"原则。开闭原则:https://blog.csdn.net/weixin_62458944/article/details/132070314?spm=1001.2014.3001.5501

什么是抽象工厂模式

抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的。抽象工厂模式可以向客户端提供一个接口,使得客户端在不必指定产品的具体类型的情况下,能够创建多个产品族的产品对象。

此处引入了一个新的概念产品族,那什么是产品族呢?百度一下:产品族是以产品平台为基础,通过添加不同的个性模块,以满足不同客户个性化需求的一组相关产品。

所谓产品族通俗来说即是:具有某一共性的一系列相关产品.以前面的Apple(苹果),Banana(香蕉),Pear(梨)为例,Apple(苹果),Banana(香蕉),Pear(梨)这三种水果对应上图中的产品等级结构。

这三种水果有产自南方的,也有产自北方的,北方和南方则对应上图中的产品族,产自北方的Apple(苹果),Banana(香蕉),Pear(梨)就构成一个产品族,它们的共性是产自北方,同样产自南方的Apple(苹果),Banana(香蕉),Pear(梨)也构成了一个产品族。 

模式中包含的角色及其职责

  • 抽象工厂(Factory)角色:抽象工厂模式的核心,包含对多个产品等级结构的声明,任何工厂类都必须实现这个接口。
  • 具体工厂(ConcreteFactory)角色:具体工厂类是抽象工厂的一个实现,负责实例化某个产品族中的产品对象。
  • 抽象(Product)角色:抽象模式所创建的所有对象的父类,或声明所有具体产品所共有的公共接口。
  • 具体产品(ConcreteProduct)角色:抽象工厂模式所创建的真正实例。 

总结:抽象工厂中的方法对应产品等级结构,具体工厂对应产品族。

接下来用代码进行说明:保留之前工厂方法模式中的Fruit接口,用来负责描述所有水果实例应该共有的方法。

public interface Fruit {/** 采集*/public void get();
}

此时Apple(苹果)和Banana(香蕉)将不再是具体的产品类而是抽象类,因为它们还需要进一步划分北方和南方两个产品族。

public abstract class Apple implements Fruit{/** 采集*/public abstract void get();
}
public abstract class Banana implements Fruit{/** 采集*/public abstract void get();
}

再进一步细分,苹果(Apple)被具体化为北方苹果(NorthApple)和南方苹果(SouthApple)。

public class NorthApple extends Apple {public void get() {System.out.println("采集北方苹果");}}
public class SouthApple extends Apple {public void get() {System.out.println("采集南方苹果");}}

香蕉(Banana)被具体化为北方香蕉(NorthBanana)和南方香蕉(SouthBanana).

public class NorthBanana extends Banana {public void get() {System.out.println("采集北方香蕉");}}
public class SouthBanana extends Banana {public void get() {System.out.println("采集南方香蕉");}}

继续写工厂,与之前的FruitFactory有所不同,此时的FruitFactory需为每一个产品等级结构添加获取方法声明。

public interface FruitFactory {//实例化Applepublic Fruit getApple();//实例化Bananapublic Fruit getBanana();
}

为每一个产品族添加相应的工厂,NorthFruitFactory负责生产所有北方的水果,SouthFruitFactory负责生产所有南方的水果。

public class NorthFruitFactory implements FruitFactory {public Fruit getApple() {return new NorthApple();}public Fruit getBanana() {return new NorthBanana();}}
public class SouthFruitFactory implements FruitFactory {public Fruit getApple() {return new SouthApple();}public Fruit getBanana() {return new SouthBanana();}}

在客户端中进行测试,代码如下。

public class Client {public static void main(String[] args) {FruitFactory ff1 = new NorthFruitFactory();Fruit apple1 = ff1.getApple();apple1.get();Fruit banana1 = ff1.getBanana();banana1.get();FruitFactory ff2 = new SouthFruitFactory();Fruit apple2 = ff2.getApple();apple2.get();Fruit banana2 = ff2.getBanana();banana2.get();}
}

运行程序打印结果如下:

采集北方苹果
采集北方香蕉
采集南方苹果
采集南方香蕉

工厂模式的优缺点

优点

  • 封装对象的创建过程:工厂模式将对象的创建过程封装到工厂类中,避免了客户端直接依赖具体的产品类,从而提高了代码的可维护性和可扩展性。
  •  创建多个产品族或产品等级结构:当需要创建多个产品族或产品等级结构时,工厂模式可以提供一个统一的接口,方便客户端进行调用。
  •  符合开闭原则:当需要添加新的产品时,只需要增加相应的产品类和工厂方法即可,不需要修改原有的代码,符合开闭原则。

缺点

  1.  增加代码复杂度:工厂模式需要增加额外的工厂类,增加了代码的复杂度。
  2.  增加系统的抽象性和理解难度:由于工厂模式引入了抽象层,因此增加了系统的抽象性和理解难度。

工厂模式运用场景

工厂模式适用于以下场景:

  1. 需要创建多个产品族或产品等级结构:当需要创建多个产品族或产品等级结构时,工厂模式可以提供一个统一的接口,方便客户端进行调用。
  2. 需要封装对象的创建过程:当对象的创建过程比较复杂,或者需要依赖其他类的时候,可以使用工厂模式来封装对象的创建过程。
  3. 需要动态切换产品:当需要动态切换产品时,工厂模式可以提供一个统一的接口,方便客户端进行调用。

总结

工厂模式是一种常见的设计模式,用于创建对象的过程中,通过工厂类来封装对象的创建过程。工厂模式具有封装对象的创建过程、创建多个产品族或产品等级结构、符合开闭原则等优点,同时也存在增加代码复杂度、增加系统的抽象性和理解难度等缺点。在实际的开发中,我们可以根据具体的需求来选择使用工厂模式或其他设计模式。

http://www.lryc.cn/news/107528.html

相关文章:

  • uniapp使用阿里图标
  • 20230803激活手机realme GT Neo3
  • Spring Cloud Feign+Ribbon的超时机制
  • 使用docker 搭建nginx + tomcat 集群
  • 从Spring的角度看Memcached和Redis及操作
  • 【C语言学习】C语言的基础数据类型
  • 使用AIGC工具提升安全工作效率
  • HBase概述
  • el-popover全屏不显示(bug记录)
  • react中使用redux-persist做持久化储存
  • 【leetcode】203. 移除链表元素(easy)
  • InfiniBand、UCIe相关思考
  • [C++项目] Boost文档 站内搜索引擎(3): 建立文档及其关键字的正排 倒排索引、jieba库的安装与使用...
  • el-date-picker回显问题解决记录
  • Linux中的特殊进程(孤儿进程、僵尸进程、守护进程)
  • 【分布式能源选址与定容】光伏、储能双层优化配置接入配电网研究(Matlab代码实现)
  • 《cuda c编程权威指南》04 - 使用块和线程索引映射矩阵索引
  • mysql 、sql server 常见的区别
  • 查找特定元素——C++ 算法库(std::find_if)
  • D3JS教程_编程入门自学教程_菜鸟教程-免费教程分享
  • Android入门教程||Android 架构||Android 应用程序组件
  • C语言二进制数据和16进制字符串互转
  • 技术复盘(5)--git
  • GDAL C++ API 学习之路 (5) Spatial Reference System篇 OGRSpatialReference类
  • 2023年华数杯数学建模C题思路代码分析 - 母亲身心健康对婴儿成长的影响
  • WebAgent-基于大型语言模型的代理程序
  • 智慧~经典开源项目数字孪生智慧商场——开源工程及源码
  • LeetCode--剑指Offer75(1)
  • C++ 关于大端模式和小端模式的简析
  • 嵌入式:C高级 Day2