浅谈简单工厂,工厂方法,抽象工厂的区别和使用
工厂模式是分为三种,分别是简单工厂,工厂方法,抽象工厂。其中工厂方法和抽象工厂是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类图中不难看出,我们如果需要拓展抽象工厂里面的方法会比较麻烦,因为我们必须修改抽象类以及添加对应的产品等级,这样修改量比较大,但是每种产品之间相互解耦,符合程序设计的“高内聚低耦合”的思想。
最后,不管是抽象工厂还是工厂方法甚至是简单工厂,他们的存在都有一定的优缺点,在设计程序的时候要根据具体情况进行取舍,不存在那种设计好那种设计特别差,只是针对不同的业务场景的不一样的处理。