工厂模式,是编程中最常用的设计模式之一。这个类型的设计模式属于创建型模式,它提供了一个创建对象的最佳方式。而在工厂模式中,在创建对象时不会对客户端暴露创建对象逻辑,并且通过使用一个共有的接口来指向新创建的对象。
但是在工厂模式有三种类型:简单的工厂模式,工厂模式,抽象工厂模型。
都是返回一个对象,从返回的对象这个点上来说其与单例模式是不一样的,单例模式返回的是为一个对象,然后大家一起用,而工厂模式返回的是有相似共同的一类对象。
工厂模式的核心思想就是把创建对象和使用对象解藕,由工厂负责对象的创建,而用户只能通过接口来使用对象,这样就可以灵活应对变化的业务需求,方便代码管理、避免代码重复。
注意:因为用简单的代码实现,使用工厂模式会给人一种让代码更复杂的感觉,不过要注意在实际开发中,如果代码量很大,这种扩展就会带来便利了。
简单工厂模式是最简单的一种工厂模式,它定义了一个负责生产对象的工厂类,使用者可以根据不同参数来创建并返回不同子类,这些子类都共用一个接口(即父类)。
先来一个例子,那就是一个代工厂生产手机的模式(只是演示,所以不要考虑同一个品牌下不同的型号,直接想这个平台下只有一个型号的机子)。
public class test {
public static void main(String[] args) {
Phone phone=SimpleFactory.getphone("opp");
phone.order();
phone.send();
}
}
abstract class Phone {
String brandname;
public void order() {
System.out.println(brandname + "收到订单,订单详情以及订单价格");
}
public void send() {
System.out.println(brandname + "联系硬件供应商,按照标准生产完毕后生产发送手机给品牌商或者规定的经销商");
}
public void setBrandname(String brandname) {
this.brandname=brandname;
}
}
class Opp extends Phone {
}
class xiaomi extends Phone {
}
class SimpleFactory {
public static Phone getphone(String brandname) {
switch (brandname) {
case "opp":
Phone opp=new Opp();
// 当然这里也有一定的标准,比如放什么芯片,屏幕等,这些都品牌商不会管
opp.setBrandname(brandname);
return opp;
case "xiaomi":
// 当然这里也有一定的标准,比如放什么芯片,屏幕等,这些都品牌商不会管
Phone xiaomi=new xiaomi();
xiaomi.setBrandname(brandname);
return xiaomi;
default:
return null;
}
}
}
然后看一下其UML图
看简单工厂类的例子可以看出,可以将其简单的划分包含三种类,分别是抽象产品类(或者接口)具体品牌类,工厂类。
类 | 描述 |
---|---|
抽象产品类(phone) | 具体的产品父类,描述产品的公共接口。而例子中的无论OPP还是小米其都是手机,所以自然有相同结构 |
具体产品类(OPP,phone) | 这个是抽象类的子类,工厂类创建的目标类,描述具体的产品,虽然都有相同,但是肯定有少许不同,比如屏幕大小,以及cpu型号。因为演示所以没有补充具体不同的细节 |
简单工厂类(SimpleFactory) | 是外部调用的提供者,而根据传入的不同参数来出创建不同的具体产品实例,返回给调用者。 |
这种方式自然尤其优点和缺点:
优点:
让对象创建和使用过程分开,实现了解耦,也方便的用户使用对象而不用关心对象是如何创建的,用的时候通过工厂类的静态方法就可以得到。简单工厂模式虽然增加了一个工厂类,但是也没有让代码过于复杂,很容易维护。
缺点:
工厂类实需要选择创建具某个对象的,所以只要添加新的对象就需要对其逻辑进行贵,如果过多的化其逻辑也会复杂。有时候工厂类创建某一个具体类出现而不能工作的时候也会影响生成其它对象。
所以其有适用的场景就是,具体品类较少(也就是返回的实例种类少),通过简单的工厂模式就可以实现生产者和消费者的分离,这样也不会再工厂类中写太复杂的判断逻辑代码。而使用者也不关心具体如何创建对象,只想传入参数直接得到需要的对象。
其实这个相当于简单的工厂模式是:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
而这种方式主要解决接口选择的问题。毕竟简单模式虽然也能通过复杂的逻辑实现不同的对象创建,但是其带来的逻辑复杂度,对于维护和代码可读性都是痛苦。
还是老规矩先上代码,然后再解释(其实这里应该具体抽象类两者不是统一族会更好,但是为了方便改了):
public class test {
public static void main(String[] args) {
Factory pcFactory= new PCFactory();
PC lenovo= (PC) pcFactory.getelectric("Lenovo");
lenovo.order();
lenovo.send();
Factory phoneFactory= new PhoneFactory();
Phone opp= (Phone) phoneFactory.getelectric("opp");
opp.order();
opp.send();
}
}
abstract class Phone {
String brandname;
public void order() {
System.out.println(brandname + "---收到手机订单,订单详情以及订单价格");
}
public void send() {
System.out.println(brandname + "联系手机硬件供应商,按照标准生产完毕后生产发送手机给品牌商或者规定的经销商");
}
public void setBrandname(String brandname) {
this.brandname=brandname;
}
}
class Opp extends Phone {
}
class xiaomi extends Phone {
}
abstract class PC {
String brandname;
public void order() {
System.out.println(brandname + "----收到pc订单,订单详情以及订单价格");
}
public void send() {
System.out.println(brandname + "----联系pc硬件供应商,按照标准生产完毕后生产发送手机给品牌商或者规定的经销商");
}
public void setBrandname(String brandname) {
this.brandname=brandname;
}
}
class Lenovo extends PC {
}
class Dell extends PC{
}
abstract class Factory {
// 抽象方法是否通过static修饰的
abstract Object getelectric(String brandname);
}
class PCFactory extends Factory{
public PC getelectric(String brandname) {
switch (brandname) {
case "Lenovo":
PC lenovo = new Lenovo();
// 当然这里也有一定的标准,比如放什么芯片,屏幕等,这些都品牌商不会管
lenovo.setBrandname(brandname);
return lenovo;
case "Dell":
// 当然这里也有一定的标准,比如放什么芯片,屏幕等,这些都品牌商不会管
PC dell = new Dell();
dell.setBrandname(brandname);
return dell;
default:
return null;
}
}
}
class PhoneFactory extends Factory {
public Phone getelectric(String brandname) {
switch (brandname) {
case "opp":
Phone opp=new Opp();
// 当然这里也有一定的标准,比如放什么芯片,屏幕等,这些都品牌商不会管
opp.setBrandname(brandname);
return opp;
case "xiaomi":
// 当然这里也有一定的标准,比如放什么芯片,屏幕等,这些都品牌商不会管
Phone xiaomi=new xiaomi();
xiaomi.setBrandname(brandname);
return xiaomi;
default:
return null;
}
}
}
可以先看一下运行的结果
然后看一下UML:
可以看出工厂模式包含了四种类分别是抽象产品类,具体产品类,抽象工厂类以及最后的具体工厂类。
类 | 描述 |
---|---|
产品抽象类(phone 和pc) | 抽象产品的父类,描述此类产品的公告接口,方便使用。 |
具体产品类(opp,xiaomi,dell,lenove) | 这个是抽象类的子类也是具体的产品,也是工厂创建的目标类,毕竟其才说描述了具体的产品 |
抽象工厂类(factory) | 具体工厂的父类,其描述了具体工厂的公共接口,不过是抽象类所以不可用static修饰。 |
具体工厂(phonefactory pcfactory) | 抽象工厂的子类,共外界调用,用来创建具体的产品实例。 |
当然这种写法也有其优缺点:
优点
其自然有简单工厂的优点,就是可以通过工厂返回自己想要的对象。其还富含的开闭原则,新增一个产品的时,只需要增加具体产品类以及工厂子类,自然也不需要去想简单工厂模式那样修改工厂的已有的代码,也不需要像是简单工厂模式中那样通过判断语句而让代码变得复杂的逻辑判断。
同时其不是静态方法,也可以形继承的等级结构,后面也可以通过继承而减少代码的重复。
缺点:
每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。
同时用户也要知道自己想要的对象对应的具体实现工厂。
所以其使用场景如下:
当然工厂类的特性也是前提就是对某个对象再被使用的时候不关心其创建过程。然后就是需要有较高的扩展性
在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。
其目的是提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。注意借鉴结构选择问题。
public class test {
public static void main(String[] args) {
Factory pcFactory= new PCFactory();
Electric lenovo= pcFactory.getelectric("Lenovo");
lenovo.order();
lenovo.send();
Factory phoneFactory= new PhoneFactory();
Electric opp= phoneFactory.getelectric("opp");
opp.order();
opp.send();
}
}
abstract class Electric{
String brandname;
abstract void order();
abstract void send();
public void setBrandname(String brandname) {
this.brandname=brandname;
}
}
abstract class Phone extends Electric{
public void order() {
System.out.println(brandname + "---收到手机订单,订单详情以及订单价格");
}
public void send() {
System.out.println(brandname + "联系手机硬件供应商,按照标准生产完毕后生产发送手机给品牌商或者规定的经销商");
}
}
class Opp extends Phone {
}
class xiaomi extends Phone {
}
abstract class PC extends Electric{
public void order() {
System.out.println(brandname + "----收到pc订单,订单详情以及订单价格");
}
public void send() {
System.out.println(brandname + "----联系pc硬件供应商,按照标准生产完毕后生产发送手机给品牌商或者规定的经销商");
}
}
class Lenovo extends PC {
}
class Dell extends PC{
}
abstract class Factory {
// 抽象方法是否通过static修饰的
abstract Electric getelectric(String brandname);
}
class PCFactory extends Factory{
public PC getelectric(String brandname) {
switch (brandname) {
case "Lenovo":
PC lenovo = new Lenovo();
// 当然这里也有一定的标准,比如放什么芯片,屏幕等,这些都品牌商不会管
lenovo.setBrandname(brandname);
return lenovo;
case "Dell":
// 当然这里也有一定的标准,比如放什么芯片,屏幕等,这些都品牌商不会管
PC dell = new Dell();
dell.setBrandname(brandname);
return dell;
default:
return null;
}
}
}
class PhoneFactory extends Factory {
public Phone getelectric(String brandname) {
switch (brandname) {
case "opp":
Phone opp=new Opp();
// 当然这里也有一定的标准,比如放什么芯片,屏幕等,这些都品牌商不会管
opp.setBrandname(brandname);
return opp;
case "xiaomi":
// 当然这里也有一定的标准,比如放什么芯片,屏幕等,这些都品牌商不会管
Phone xiaomi=new xiaomi();
xiaomi.setBrandname(brandname);
return xiaomi;
default:
return null;
}
}
}
可以先看一下运行的结果
可以看出其满足了我们的需求,然后下面再看一下其UML:
可以看出工 模式包含5种类分别是抽象产品类的族类,抽象产品类,具体产品类,抽象工厂类以及最后的具体工厂类。
但是上面例子中还将
抽象产品类中的共同点再次写了一个上层的抽象类,因为例子中其调用的方法是相同的。所以不冲突说四种也行,如果较真说五种也可以。
类 | 描述 |
---|---|
抽象产品类的族类(Electric) | 定义了这一类产量的标准,所以其产品族难扩展,产品等级易扩展。比如需要生成食物的时候无法时候,因为工厂类中返回的电子产品,算是非同族产品。 |
产品抽象类(phone 和pc) | 抽象产品的父类,描述此类产品的公告接口,方便使用。 |
具体产品类(opp,xiaomi,dell,lenove) | 这个是抽象类的子类也是具体的产品,也是工厂创建的目标类,毕竟其才说描述了具体的产品 |
抽象工厂类(factory) | 具体工厂的父类,其描述了具体工厂的公共接口,不过是抽象类所以不可用static修饰。 |
具体工厂(phonefactory pcfactory) | 抽象工厂的子类,共外界调用,用来创建具体的产品实例。 |
当然这种写法也有其优缺点:
优点
其自然有简单工厂以及工厂模式的优点,就是可以通过工厂返回自己想要的对象。其还富含的开闭原则,新增一个产品的时,只需要增加具体产品类以及工厂子类,自然也不需要去想简单工厂模式那样修改工厂的已有的代码,也不需要像是简单工厂模式中那样通过判断语句而让代码变得复杂的逻辑判断。
同时其不是静态方法,也可以形继承的等级结构,后面也可以通过继承而减少代码的重复。
缺点:
每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。
同时用户也要知道自己想要的对象对应的具体实现工厂。同时产品族难扩展,产品等级易扩展。
所以其使用场景如下:
当然工厂类的特性也是前提就是对某个对象再被使用的时候不关心其创建过程。同时系统的产品有多于一个的产品族,而系统只消费其中某一族的产品同时也需要有较高的扩展性。
简单工厂模式:让一个工厂类负责创建所有对象;但没有考虑后期扩展和维护,修改违背开闭原则,静态方法不能被继承。
工厂方法模式:主要思想是继承,修改符合开闭原则;但每个工厂只能创建一种类型的产品。其适合的是工厂的的对象一般是非同族的对象。
抽象工厂模式:主要思想是组合,本质是产品族,实际包含了很多工厂方法,修改符合开闭原则;但只适用于增加同类工厂这种横向扩展需求,不适合新增新非同族类这种纵向扩展。
作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。所以这个就是看自己项目的具体需求,不要为了盲目炫技直接使用工厂模式