简单工厂、工厂方法、抽象工厂、策略模式、策略与工厂的区别
结合简单示例和UML图,讲解工厂模式简单原理。
一、引子
话说十年前,有一个爆发户,他家有三辆汽车(Benz(奔驰)、Bmw(宝马)、Audi(奥迪)),还雇了司机为他开车。不过,爆发户坐车时总是这样:上Benz车后跟司机说“开奔驰车!”,坐上Bmw后他说“开宝马车!”,坐上 Audi后他说“开奥迪车!”。
你一定说:这人有病!直接说开车不就行了?!而当把这个爆发户的行为放到我们程序语言中来,我们发现C语言一直是通过这种方式来坐车的!
幸运的是这种有病的现象在OO语言中可以避免了。下面以Java语言为基础来引入我们本文的主题:工厂模式!
二、简介
工厂模式主要是为创建对象提供了接口。工厂模式按照《Java与模式》中的提法分为三类:
1. 简单工厂模式(Simple Factory)
2. 工厂方法模式(Factory Method)
3. 抽象工厂模式(Abstract Factory)
这三种模式从上到下逐步抽象,并且更具一般性。还有一种分类法,就是将简单工厂模式看为工厂方法模式的一种特例,两个归为一类。两者皆可,这本为使用《Java与模式》的分类方法。
在什么样的情况下我们应该记得使用工厂模式呢?大体有两点:
1.在编码时不能预见需要创建哪种类的实例。
2.系统不应依赖于产品类实例如何被创建、组合和表达的细节
工厂模式能给我们的OOD、OOP带来哪些好处呢??
三、简单工厂模式
这个模式本身很简单而且使用在业务较简单的情况下。一般用于小项目或者具体产品很少扩展的情况(这样工厂类才不用经常更改)。
它由三种角色组成:
工厂类角色:这是本模式的核心,含有一定的商业逻辑和判断逻辑,根据逻辑不同,产生具体的工厂产品。如例子中的Driver类。
抽象产品角色:它一般是具体产品继承的父类或者实现的接口。由接口或者抽象类来实现。如例中的Car接口。
具体产品角色:工厂类所创建的对象就是此角色的实例。在java中由一个具体类实现,如例子中的Benz、Bmw类。
来用类图来清晰的表示下的它们之间的关系:
下面就来给那个暴发户治病:在使用了简单工厂模式后,现在暴发户只需要坐在车里对司机说句:“开车”就可以了。来看看怎么用代码实现的:(为方便起见,所有的类放在一个文件中,故有一个类被声明为public)
- //抽象产品
- abstract class Car{
- private String name;
- public abstract void drive();
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
- //具体产品
- class Benz extends Car{
- public void drive(){
- System.out.println(this.getName()+"----go-----------------------");
- }
- }
- class Bmw extends Car{
- public void drive(){
- System.out.println(this.getName()+"----go-----------------------");
- }
- }
- //简单工厂
- class Driver{
- public static Car createCar(String car){
- Car c = null;
- if("Benz".equalsIgnoreCase(car))
- c = new Benz();
- else if("Bmw".equalsIgnoreCase(car))
- c = new Bmw();
- return c;
- }
- }
- //老板
- public class BossSimplyFactory {
- public static void main(String[] args) throws IOException {
- //老板告诉司机我今天坐奔驰
- Car car = Driver.createCar("benz");
- car.setName("benz");
- //司机开着奔驰出发
- car.drive();
- }
- <span style="font-family: courier new,courier;">}</span>
如果老板要坐奥迪,同理。
这便是简单工厂模式了。那么它带了了什么好处呢?
首先,符合现实中的情况;而且客户端免除了直接创建产品对象的责任,而仅仅负责“消费”产品(正如暴发户所为)。
下面我们从开闭原则上来分析下简单工厂模式。当暴发户增加了一辆车的时候,只要符合抽象产品制定的合同,那么只要通知工厂类知道就可以被客户使用了。(即创建一个新的车类,继承抽象产品Car)那么 对于产品部分来说,它是符合开闭原则的——对扩展开放、对修改关闭;但是工厂类不太理想,因为每增加一辆车,都要在工厂类中增加相应的商业逻辑和判 断逻辑,这显自然是违背开闭原则的。
而在实际应用中,很可能产品是一个多层次的树状结构。由于简单工厂模式中只有一个工厂类来对应这些产品,所以这可能会把我们的上帝类坏了。
正如我前面提到的简单工厂模式适用于业务简单的情况下或者具体产品很少增加的情况。而对于复杂的业务环境可能不太适应了。这就应该由工厂方法模式来出场了!!
四、工厂方法模式
抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。
具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。在java中它由具体的类来实现。
抽象产品角色:它是具体产品继承的父类或者是实现的接口。在java中一般有抽象类或者接口来实现。
具体产品角色:具体工厂角色所创建的对象就是此角色的实例。在java中由具体的类来实现。
来用类图来清晰的表示下的它们之间的关系:
话说暴发户生意越做越大,自己的爱车也越来越多。这可苦了那位司机师傅了,什么车它都要记得,维护,都要经过他来使用!于是暴发户同情他说:我给你分配几个人手,你只管管好他们就行了!于是工厂方法模式的管理出现了。代码如下:
- //抽象产品
- abstract class Car{
- private String name;
- public abstract void drive();
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
- //具体产品
- class Benz extends Car{
- public void drive(){
- System.out.println(this.getName()+"----go-----------------------");
- }
- }
- class Bmw extends Car{
- public void drive(){
- System.out.println(this.getName()+"----go-----------------------");
- }
- }
- //抽象工厂
- abstract class Driver{
- public abstract Car createCar(String car) throws Exception;
- }
- //具体工厂(每个具体工厂负责一个具体产品)
- class BenzDriver extends Driver{
- public Car createCar(String car) throws Exception {
- return new Benz();
- }
- }
- class BmwDriver extends Driver{
- public Car createCar(String car) throws Exception {
- return new Bmw();
- }
- }
- //老板
- public class Boss{
- public static void main(String[] args) throws Exception {
- Driver d = new BenzDriver();
- Car c = d.createCar("benz");
- c.setName("benz");
- c.drive();
- }
- }
使用开闭原则来分析下工厂方法模式。当有新的产品(即暴发户的汽车)产生时,只要按照抽象产品角色、抽象工厂角色提供的合同来生成,那么就可以被客户使用,而不必去修改任何已有的代码。(即当有新产品时,只要创建并基础抽象产品;新建具体工厂继承抽象工厂;而不用修改任何一个类)工厂方法模式是完全符合开闭原则的!
使用工厂方法模式足以应付我们可能遇到的大部分业务需求。但是当产品种类非常多时,就会出现大量的与之对应的工厂类,这不应该是我们所希望的。所以我建议在这种情况下使用简单工厂模式与工厂方法模式相结合的方式来减少工厂类:即对于产品树上类似的种类(一般是树的叶子中互为兄弟的)使用简单工厂模式来实现。
当然特殊的情况,就要特殊对待了:对于系统中存在不同的产品树,而且产品树上存在产品族(下一节将解释这个名词)。那么这种情况下就可能可以使用抽象工厂模式了。
五、小结
让我们来看看简单工厂模式、工厂方法模式给我们的启迪:
如果不使用工厂模式来实现我们的例子,也许代码会减少很多——只需要实现已有的车,不使用多态。但是在可维护性上,可扩展性上是非常差的(你可以想象一下添加一辆车后要牵动的类)。因此为了提高扩展性和维护性,多写些代码是值得的。
六、抽象工厂模式
先来认识下什么是产品族: 位于不同产品等级结构中,功能相关联的产品组成的家族。
图中的BmwCar和BenzCar就是两个产品树(产品层次结构);而如图所示的BenzSportsCar和BmwSportsCar就是一个产品族。他们都可以放到跑车家族中,因此功能有所关联。同理BmwBussinessCar和BenzBusinessCar也是一个产品族。可以这么说,它和工厂方法模式的区别就在于需要创建对象的复杂程度上。而且抽象工厂模式是三个里面最为抽象、最具一般性的。抽象工厂模式的用意为:给客户端提供一个接口,可以创建多个产品族中的产品对象。
而且使用抽象工厂模式还要满足一下条件:
1.系统中有多个产品族,而系统一次只可能消费其中一族产品
2.同属于同一个产品族的产品以其使用。
来看看抽象工厂模式的各个角色(和工厂方法的如出一辙):
抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。
具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。在java中它由具体的类来实现。
抽象产品角色:它是具体产品继承的父类或者是实现的接口。在java中一般有抽象类或者接口来实现。
具体产品角色:具体工厂角色所创建的对象就是此角色的实例。在java中由具体的类来实现。
- //抽象产品(Bmw和Audi同理)
- abstract class BenzCar{
- private String name;
- public abstract void drive();
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
- //具体产品(Bmw和Audi同理)
- class BenzSportCar extends BenzCar{
- public void drive(){
- System.out.println(this.getName()+"----BenzSportCar-----------------------");
- }
- }
- class BenzBusinessCar extends BenzCar{
- public void drive(){
- System.out.println(this.getName()+"----BenzBusinessCar-----------------------");
- }
- }
- abstract class BmwCar{
- private String name;
- public abstract void drive();
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
- class BmwSportCar extends BmwCar{
- public void drive(){
- System.out.println(this.getName()+"----BmwSportCar-----------------------");
- }
- }
- class BmwBusinessCar extends BmwCar{
- public void drive(){
- System.out.println(this.getName()+"----BmwBusinessCar-----------------------");
- }
- }
- abstract class AudiCar{
- private String name;
- public abstract void drive();
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
- class AudiSportCar extends AudiCar{
- public void drive(){
- System.out.println(this.getName()+"----AudiSportCar-----------------------");
- }
- }
- class AudiBusinessCar extends AudiCar{
- public void drive(){
- System.out.println(this.getName()+"----AudiBusinessCar-----------------------");
- }
- }
- //抽象工厂
- abstract class Driver3{
- public abstract BenzCar createBenzCar(String car) throws Exception;
- public abstract BmwCar createBmwCar(String car) throws Exception;
- public abstract AudiCar createAudiCar(String car) throws Exception;
- }
- //具体工厂
- class SportDriver extends Driver3{
- public BenzCar createBenzCar(String car) throws Exception {
- return new BenzSportCar();
- }
- public BmwCar createBmwCar(String car) throws Exception {
- return new BmwSportCar();
- }
- public AudiCar createAudiCar(String car) throws Exception {
- return new AudiSportCar();
- }
- }
- class BusinessDriver extends Driver3{
- public BenzCar createBenzCar(String car) throws Exception {
- return new BenzBusinessCar();
- }
- public BmwCar createBmwCar(String car) throws Exception {
- return new BmwBusinessCar();
- }
- public AudiCar createAudiCar(String car) throws Exception {
- return new AudiBusinessCar();
- }
- }
- //老板
- public class BossAbstractFactory {
- public static void main(String[] args) throws Exception {
- Driver3 d = new BusinessDriver();
- AudiCar car = d.createAudiCar("");
- car.drive();
- }
- }
UML图略。
- abstract class BenzCar{
- private String name;
- public abstract void drive();
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
- class BenzSportCar extends BenzCar{
- public void drive(){
- System.out.println(this.getName()+"----BenzSportCar-----------------------");
- }
- }
- class BenzBusinessCar extends BenzCar{
- public void drive(){
- System.out.println(this.getName()+"----BenzBusinessCar-----------------------");
- }
- }
- abstract class BmwCar{
- private String name;
- public abstract void drive();
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
- class BmwSportCar extends BmwCar{
- public void drive(){
- System.out.println(this.getName()+"----BmwSportCar-----------------------");
- }
- }
- class BmwBusinessCar extends BmwCar{
- public void drive(){
- System.out.println(this.getName()+"----BmwBusinessCar-----------------------");
- }
- }
- abstract class AudiCar{
- private String name;
- public abstract void drive();
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
- class AudiSportCar extends AudiCar{
- public void drive(){
- System.out.println(this.getName()+"----AudiSportCar-----------------------");
- }
- }
- class AudiBusinessCar extends AudiCar{
- public void drive(){
- System.out.println(this.getName()+"----AudiBusinessCar-----------------------");
- }
- }
- /**
- * 简单工厂通过反射改进抽象工厂及其子工厂
- * @author Administrator
- *
- */
- class Driver3{
- public static BenzCar createBenzCar(String car) throws Exception {
- return (BenzCar) Class.forName(car).newInstance();
- }
- public static BmwCar createBmwCar(String car) throws Exception {
- return (BmwCar) Class.forName(car).newInstance();
- }
- public static AudiCar createAudiCar(String car) throws Exception {
- return (AudiCar) Class.forName(car).newInstance();
- }
- }
- //客户端
- public class SimpleAndAbstractFactory {
- public static void main(String[] args) throws Exception {
- AudiCar car = Driver3.createAudiCar("com.java.pattendesign.factory.AudiSportCar");
- car.drive();
- }
- }
举例:导出成EXCEL,WORD,PDF文件的功能,这三类导出虽然具体操作略有不同,但是大部分都相同。
- //抽象产品
- abstract class AudiCar{
- private String name;
- public abstract void makeCar();
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
- //具体产品
- class AudiA6 extends AudiCar{
- public void makeCar(){
- System.out.println(this.getName()+"----go-----------------------");
- }
- }
- class AudiA4 extends AudiCar{
- public void makeCar(){
- System.out.println(this.getName()+"----go-----------------------");
- }
- }
- //销售部门----服务端
- class CarContext {
- AudiCar audiCar = null;
- public CarContext(AudiCar audiCar) {
- this.audiCar = audiCar;
- }
- public void orderCar(){
- this.audiCar.makeCar();
- }
- }
- //客户----客户端(这个客户是内行,什么都懂,他说我要A6,销售部门立刻给他a6,所以销售部门不用很懂)
- public class SimplyFactoryAndStrategy2 {
- public static void main(String[] args) throws IOException {
- //客户说我要什么什么样子的车子,销售人员才知道他要什么样子的车子
- AudiCar car = new AudiA6();
- car.setName("a6");
- CarContext context = new CarContext(car);
- context.orderCar();
- }
- }
- //工厂模式---与上面的策略模式比较
- //抽象产品
- abstract class AudiCar{
- private String name;
- public abstract void makeCar();
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
- //具体产品
- class AudiA6 extends AudiCar{
- public void makeCar(){
- System.out.println(this.getName()+"----go-----------------------");
- }
- }
- class AudiA4 extends AudiCar{
- public void makeCar(){
- System.out.println(this.getName()+"----go-----------------------");
- }
- }
- //简单工厂---销售部门----服务端
- class CarFactroy{
- public static AudiCar createCar(String car){
- AudiCar c = null;
- if("A6".equalsIgnoreCase(car))
- c = new AudiA6();
- else if("A4".equalsIgnoreCase(car))
- c = new AudiA4();
- return c;
- }
- }
- //客户----客户端(这个客户是外行,什么都不懂,只要随便描述下车,销售部门才能知道他要那款车,所以销售部门比较牛)
- public class SimplyFactoryAndStrategy {
- public static void main(String[] args) throws IOException {
- System.out.print("请输入您要坐的车:(A6、A4)");
- String carName = new BufferedReader(new InputStreamReader(System.in)).readLine();
- //客户说我要什么什么样子的车子,销售人员才知道他要什么样子的车子
- AudiCar car = CarFactroy.createCar(carName);
- car.setName(carName);
- car.makeCar();
- }
- }
策略模式的优缺点
策略模式的主要优点有:
- 策略类之间可以自由切换,由于策略类实现自同一个抽象,所以他们之间可以自由切换。
- 易于扩展,增加一个新的策略对策略模式来说非常容易,基本上可以在不改变原有代码的基础上进行扩展。
- 避免使用多重条件,如果不使用策略模式,对于所有的算法,必须使用条件语句进行连接,通过条件判断来决定使用哪一种算法,在上一篇文章中我们已经提到,使用多重条件判断是非常不容易维护的。
策略模式的缺点主要有两个:
- 维护各个策略类会给开发带来额外开销,可能大家在这方面都有经验:一般来说,策略类的数量超过5个,就比较令人头疼了。
- 必须对 客户端(调用者)暴露所有的策略类,因为使用哪种策略是由客户端来决定的,因此,客户端应该知道有什么策略,并且了解各种策略之间的区别,否则,后果很严 重。例如,有一个排序算法的策略模式,提供了快速排序、冒泡排序、选择排序这三种算法,客户端在使用这些算法之前,是不是先要明白这三种算法的适用情况? 再比如,客户端要使用一个容器,有链表实现的,也有数组实现的,客户端是不是也要明白链表和数组有什么区别?就这一点来说是有悖于迪米特法则的。
适用场景
做面向对象设计的,对策略模式一定很熟悉,因为它实质上就是面向对象中的继承和多态,在看完策略模式的通用代码后,我想,即使之前从来没有听说过策略模式,在开发过程中也一定使用过它吧?至少在在以下两种情况下,大家可以考虑使用策略模式,
- 几个类的主要逻辑相同,只在部分逻辑的算法和行为上稍有区别的情况。
- 有几种相似的行为,或者说算法,客户端需要动态地决定使用哪一种,那么可以使用策略模式,将这些算法封装起来供客户端调用。
策略模式是一种简单常用的模式,我们在进行开发的时候,会经常有意无意地使用它,一般来说,策略模式不会单独使用,跟模版方法模式、工厂模式等混合使用的情况比较多。
大粒度的 if --else if...可以使用 工厂+策略模式搞定。
相关推荐
本文将探讨三个重要的设计模式:抽象工厂模式、工厂方法模式以及策略模式,并结合一个实际的场景——手机加工厂,来具体阐述它们的应用。 首先,我们来看**抽象工厂模式**。这个模式主要用于创建相关或依赖对象的...
在软件设计模式中,工厂模式是一种非常...在实际项目中,工厂模式经常与其他设计模式(如策略模式、建造者模式等)结合使用,以解决更复杂的问题。阅读并理解这些代码有助于深入理解工厂模式及其在Java编程中的应用。
本案例主要探讨了三种经典的设计模式:抽象工厂模式、工厂方法模式和策略模式,并以手机加工厂为实际应用场景进行阐述。 首先,**抽象工厂模式**是创建型设计模式之一,它提供一个接口,用于创建相关或依赖对象的...
3. **抽象工厂**:抽象工厂模式属于创建型设计模式,提供一个接口来创建相关或依赖对象的家族,而无需指定具体类。它允许更换产品族,同时保持相同的接口。 4. **单例**:单例模式确保一个类只有一个实例,并提供一...
抽象工厂模式、建造模式、工厂方法模式、原型模式、单例模式、外观模式、适配器模式、桥接模式、组合模式、装饰模式、享元模式、代理模式、命令模式、解释器模式、访问者模式、迭代子模式、中介者模式、备忘录模式、...
python 项目里常用的设计模式 单例模式 组合模式 策略模式 抽象工厂 代理模式 工厂模式 观察者模式简单工厂 建造者模式 模板模式 桥接多维度 桥接模式 适配器模式 外观模式 享元模式 责任链组合模式
在软件设计模式中,工厂方法(Factory Method)和抽象工厂(Abstract Factory)是两种非常重要的创建型模式。它们主要用于解决对象实例化的问题,提供了一种封装对象创建过程的方法,使得代码更加灵活,易于扩展和...
工厂模式通常分为简单工厂模式、工厂方法模式和抽象工厂模式三种类型。在实际项目中,例如数据库连接的创建,我们可以使用工厂模式来创建不同类型的连接对象,如MySQL连接、Oracle连接等,而无需关心具体的实现细节...
在C++中,我们还可以利用模板方法和策略模式等设计模式来进一步优化抽象工厂模式,比如将产品创建过程封装到单独的模板方法中,或者通过配置文件动态地选择具体工厂,以提高系统的灵活性。 总的来说,C++中的抽象...
使用抽象工厂模式而非工厂方法模式的原因在于,工厂方法模式只能处理单一的产品等级结构,而抽象工厂模式可以处理多个相关产品等级结构,也就是产品族。在格斗游戏中,产品族可能是不同难度级别的敌人,而产品等级...
ASP.NET CMS 使用抽象工厂模式开发是一种常见的软件设计策略,它在构建可扩展和灵活的Web内容管理系统时扮演着重要角色。抽象工厂模式是设计模式中的一种,它提供了一个创建一系列相关或相互依赖对象的接口,而无需...
在实际应用中,我们可以通过使用策略模式、工厂方法模式或依赖注入框架来优化抽象工厂模式,以提高其灵活性和可扩展性。 总的来说,抽象工厂设计模式是一种强大的工具,它能够帮助我们构建更灵活、模块化的软件系统...
在实际开发中,抽象工厂模式可以与其他设计模式结合使用,如策略模式,以处理更复杂的条件判断和行为选择。此外,它也广泛应用于框架和库的设计,如ORM(对象关系映射)工具,以适应多种数据库系统。
此外,工厂方法还可以与其他设计模式结合,如策略模式、单例模式等,以解决更复杂的问题。 总的来说,工厂方法模式是创建型设计模式中的基础,对于理解和掌握其他高级设计模式有着重要的铺垫作用。通过不断地实践和...
PHP 设计模式实例之观察者模式、策略模式、简单工厂模式 PHP 设计模式实例之观察者模式、...观察者模式用于在对象之间定义一对多的依赖关系,策略模式用于定义一系列的算法,简单工厂模式用于提供一个创建对象的接口。
在C#三层架构中,抽象工厂模式可以用来创建不同环境下的数据访问组件,例如在不同的数据库(如SQL Server、Oracle)之间切换,或者在不同的缓存策略(如内存缓存、分布式缓存)之间选择,无需修改业务逻辑层代码。...
在C#编程语言中,这种模式常用于当系统需要与多个平台或框架交互时,抽象工厂可以帮助我们隔离具体实现,提供更加灵活的代码结构。 三层架构,又称为N-Tier架构,是一种常见的软件架构设计方式,将应用程序分为三个...
本篇主要探讨的是“抽象工厂”设计模式,这是一种创建型设计模式,它提供一个接口用于创建相关或依赖对象的家族,而无需指定具体类。 抽象工厂模式的核心概念是“工厂”,它不直接生产产品,而是提供一个创建产品的...