浅谈简单工厂,工厂方法,抽象工厂的区别和使用

  工厂模式是分为三种,分别是简单工厂,工厂方法,抽象工厂。其中工厂方法和抽象工厂是GoF23种设计模式中的一种,而简单工厂则不是一种设计模式,更加可以理解的是一种编码时候预定俗称的一种习惯。那么,就在接下来三点中分别去分析理解工厂模式。

一 简单工厂:通过实例化一个工厂类,来获取对应的产品实例。我们不需要关注产品本身如何被创建的细节,只需要通过相应的工厂就可以获得相应的实例。简单工厂包括三种角色:

1.工厂:简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。  

2.抽象产品 :简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。

3.具体产品:是简单工厂模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。

比如以下例子:

        

                            (类设计的UML图) 

1.Drinks作为产品的抽象类并且有抽象方法produce();(抽象产品
public  abstract class Drinks {    
  protected abstract void produce();
}
2.Sprite继承Drinks是要被具体生产出来的产品,他重写了produce()方法。(具体产品)
public class Sprite extends Drinks {

@Override
protected void produce() {
System.out.println("drink sprite");
}
}

3.Cola同样也继承了Drinks,是要被生产出来的具体产品。
(具体产品)
public class Cola extends Drinks {

@Override
protected void produce() {
System.out.println("Drink Cola");
}
}

4.DrinksFactory为简单工厂,向外暴露produceDrink方法来获取产品的实例(工厂
public class DrinksFactory {

public Drinks produceDrink(Class className){
try {
return (Drinks) Class.forName(className.getName()).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}

return null;
}
}

5.Client为应用层,Client端想要获取到Cola或者Sprite对象,只要通过DrinkFactory中的produceDrink方法传入相对应的对应的产品
public class Client {
public static void main(String[] args) {
DrinksFactory factory = new DrinksFactory();
Cola cola = (Cola) factory.produceDrink(Cola.class);
cola.produce();
}
}
简单工厂的优点:
1.不需要关心类的创建细节。
2.减轻类之间的耦合依赖,具体类的实现只是依赖于简单工厂,而不依赖其他类。

简单工厂的缺点:
1.扩展复杂,当简单工厂需要生产出另外一种产品的时候,需要扩展工厂的内部创建逻辑,比较有可能引起较大的故障
2.由于工厂类集中了所有实例的创建逻辑,违反了高内聚责任分配原则,将全部创建逻辑集中到了一个工厂类中


二.工厂方法

  工厂方法的定义是:定义一个创建对象的接口,让实现这个接口的的类去决定实例化具体的类。工厂方法让类的实例化推迟到实现接口的子类中进行。

  比如说,我现在需要一瓶可乐,有可口可乐公司生产的可乐也有百事可乐公司生产的可乐,那么对于可乐这个产品等级(抽象工厂中会具体说明),具体生产什么可乐并不是在一个工厂实现,而是由一个可乐工厂指定一个标准(接口里面的抽象方法),可口可乐公司百事可乐公司只要按照这个标准去生产就可以了。   

                                               

                                             (2)工厂方法的Uml类图 

 

1.Cola此类是产品的父类
public abstract class Cola {
protected abstract void drinks();
}
2.PepsiCola继承Cola,是要生产的产品之一
public class PepsiCola extends  Cola {

@Override
protected void drinks() {
System.out.println("Drinks PepsiCola");
}
}
3.CoCoCola同样继承Cola,也是要生产的产品之一
public class CoCoCola extends Cola {

@Override
protected void drinks() {
System.out.println("Drinks cococoLa");
}
}
4.ColaFacotry定义抽象工厂,指定要生产此类产品的规范(存在的方法与属性),指定工厂方法
public interface ColaFacotry {

Cola produce(Class<Cola> cola);
}
5.PepsiColaFactory定义子类工厂,它继承抽象工厂,实现了对某一产品等级的产品的获得
public class PepsiColaFactory implements ColaFacotry {

public PepsiCola produce(Class cola) {
if (cola.isInstance(PepsiCola.class)){
return new PepsiCola();
}
return null;
}
}
6.ColaFacotry,是规定工厂方法去获得拿一些的产品等级的商品,比如说,我规定生产的产品等级是可乐和草莓可乐,那么对于它的实现类来说,也就是其子类中的重载方法来说去具体实现获取产品的具体实现。
public interface ColaFacotry {

Cola produce(Class<Cola> cola);
}



 

三.抽象工厂

  抽象工厂是提供了创建一系列服务的的对象的接口。那么问题就来了,怎么区分和工厂方法中的服务对象宁?此时就需要对一组概念有所理解,即产品等级和产品族,我从网上找到下面这张图,进行解释说明。在图(4)中,我们可以通过横向和纵向的比较,横向是某一个手机厂商如苹果,小米等,他们不仅仅生产手机,还生产电脑,耳机等一系类产品,那么我们把苹果,小米,华为这样的厂商可以认为他们生产的是一个产品族,而他们自己本身就是一个抽象工厂的具体实现;那么纵向来看,不管是小米华为还是苹果,他们生产的产品是按照一定的规则来生产,显示屏,电池,处理器等等,所以对于纵向的产品来说,他们又是属于同一个产品等级,我们亦可以称他们的实现为工厂方法。

  综上所述,抽象工厂解决的是横向的产品族,工厂方法解决的是纵向的产品等级。具体抽象工厂请看代码。

                 

                                              (3)产品等级和产品族(图片来自https://laravel-china.org/topics/18598?order_by=created_at&) 

 

                          

                          (4)产品等级示意图

   1.AbstractFactory 抽象工厂

public  interface AbstractFactory {

public Phone producePhone();

public Computer producaComputer();
}
2.AppleFactory具体工厂的实现一
public class AppleFactory implements AbstractFactory {
public Phone producePhone() {
return new Iphone();
}

public Computer producaComputer() {
return new Mac();
}
}
3.MiFactory具体工厂的实现二
public class MiFactory implements AbstractFactory {
public Phone producePhone() {
return new MiPhone();
}

public Computer producaComputer() {
return new MiComputer();
}
}
4.Phone抽象产品等级一
public abstract class Phone {
public abstract void call();
}
5.Iphone具体产品一
public class Iphone extends Phone {

@Override
public void call() {
System.out.println("Iphone call");
}
}
6.MiPhone具体产品一
public class MiPhone extends Phone {

@Override
public void call() {
System.out.println("Mi Phone call");
}
}
7.Computer抽象产品等级二
public  abstract class Computer {

public abstract void work();
}
8.Mac具体产品一
public class Mac extends Computer {

@Override
public void work() {
System.out.println("MAC work");
}
}
9.MiComputer 具体产品二

public class MiComputer extends Computer {

@Override
public void work() {
System.out.println("MI computer word");
}
}
10.客户端
public class Clint {

public static void main(String[] args) {
AppleFactory appleFactory = new AppleFactory();
appleFactory.producaComputer().work();
appleFactory.producePhone().call();

MiFactory miFactory = new MiFactory();
miFactory.producaComputer().work();
miFactory.producePhone().call();
}
}

 

      

                        (5)抽象工厂UML类图

从UML类图中不难看出,我们如果需要拓展抽象工厂里面的方法会比较麻烦,因为我们必须修改抽象类以及添加对应的产品等级,这样修改量比较大,但是每种产品之间相互解耦,符合程序设计的“高内聚低耦合”的思想。

 

最后,不管是抽象工厂还是工厂方法甚至是简单工厂,他们的存在都有一定的优缺点,在设计程序的时候要根据具体情况进行取舍,不存在那种设计好那种设计特别差,只是针对不同的业务场景的不一样的处理。

 

posted @ 2018-11-29 08:14  程序猿灿林  阅读(16230)  评论(1编辑  收藏  举报