8分钟帮你理清工厂模式

8分钟帮你理清工厂模式

哈啰,各位小伙伴,这里是每天进步一点点的花栗鼠小K

“这几天都在忙活硕士毕业的材料,满脑子都是盖章、扫描、学位信息上报等各种要命的东西,恍惚间,就到了该交作业的日子,想着之前开了个面试经验分享的口子,但好久没有写面试的东西了。今天就补上一篇面试必问的一part:设计模式 。“

回顾整个面试,小K发现面试官的问题,问的范围还是挺集中的。一般都绕不开那么几个问题

  1. 你了解设计模式吗?
  2. 你常用哪些设计模式?
  3. 挑一些你最熟悉的设计模式讲一下
  4. AA设计模式和BB设计模式有什么区别?

面对面试官的死亡四连问,大部分人都能扛到前三问,但是没系统学过设计模式的同学,可能第四问有点吃力。

那么今天小K带大家探秘一下第四问的常客:工厂类设计模式

首先,我们先搞清楚是什么



01


什么是工厂模式



简单点

工厂模式(Factory Pattern)属于创建型模式,它提供了一种创建对象的最佳方式,使得在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象,换句话说就是实现了使用者和创建者的分离。

学习就是刚解决一个老问题,就出现一个新问题的过程,可能又有小伙伴疑惑,什么是创建型模式。好吧,如下

创建型模式(Creational Pattern)对类的实例化过程进行了抽象,能够将软件模块中对象的创建和对象的使用分离。为了使软件的结构更加清晰,外界对于这些对象只需要知道它们共同的接口,而不清楚其具体的实现细节,使整个系统的设计更加符合单一职责原则。

该模式下有三个子类

  • 简单工厂模式(Simple Factory)
  • 工厂方法模式(Factory Method)
  • 抽象工厂模式(Abstract Factory)

接下来,小K一一介绍这三个子类



02


简单工厂模式


简单工厂是由一个工厂对象决定创建哪一个产品类的实例

该模式并不是23种设计模式之一,相比于工厂方法模式抽象工厂模式的正统地位,它有点像捡来的孩纸

UML 如图

8分钟帮你理清工厂模式
简单工厂模式UML

简单工厂模式包含三个角色

Factory:即工厂类, 简单工厂模式的核心部分,负责实现创建所有产品的内部逻辑;工厂类可以被外界直接调用,创建所需对象

Product:抽象类产品, 它是工厂类所创建的所有对象的父类,封装了各种产品对象的公有方法,它的引入将提高系统的灵活性,使得在工厂类中只需定义一个通用的工厂方法,因为所有创建的具体产品对象都是其子类对象

ConcreteProduct:具体产品, 它是简单工厂模式的创建目标,所有被创建的对象都充当这个角色的某个具体类的实例。它要实现抽象产品中声明的抽象方法

举个🌰

生产一部手机,创建了手机接口和具体手机的类

public  interface MobilePhone {
public void makeCall();
public void surfInternet();
}
// 华为手机
public class HuaWei implements MobilePhone{
@Override
public void makeCall() { System.out.println("华为手机通话"); }
@Override
public void surfInternet() { System.out.println("华为手机上网"); }
}
// 苹果手机
public class Apple implements MobilePhone{
@Override
public void makeCall() { System.out.println("苹果手机通话"); }
@Override
public void surfInternet() { System.out.println("苹果手机上网"); }
}
// 小米手机
public class XiaoMi implements MobilePhone{
@Override
public void makeCall() { System.out.println("小米手机通话"); }
@Override
public void surfInternet() { System.out.println("小米手机上网"); }
}

华为、苹果、小米手机都继承自 MobilePhone ,为了便于管理对象的创建,通过一个简单的工厂模式来创建这些类型的对象。简单工厂模式的代码如下

public  class SimpleFactory {
public static MobilePhone createPhone(String brand) {
MobilePhone phone = null;
if (brand.equalsIgnoreCase("huawei")) {
phone = new HuaWei();
} else if (brand.equalsIgnoreCase("xiaomi")) {
phone = new XiaoMi();
} else if (brand.equalsIgnoreCase("apple")) {
phone = new Apple();
}
return phone;
}
}

MobileFactorycreatePhone 函数中我们需要传递一个参数,通过该参数来创建不同的手机对象,例如华为、苹果、小米。

MobileFactory 中只有一个静态函数 createPhone 通过该函数可以创建多种对象类型,这就是简单工厂模式的核心定义,所以也称为 静态方法工厂模式

简单工厂模式的优缺点

优点

  • 简单工厂模式的名称就源自于其简单
  • 明确了各自的职责和权利,有利于整个软件体系结构的优化

缺点

  • 实际应用中,很可能产品是一个多层次的树状结构, 可能不太适用了
  • 它所能创建的类只能是事先考虑到的,如果需要添加新的类,则就需要改变工厂类了
  • 当具体产品类不断增多时候,可能会出现要求工厂类根据不同条件创建不同实例的需求.这种对条件的判断和对具体产品类型的判断交错在一起,很难避免模块功能的蔓延,对系统的维护和扩展非常不利

为了解决上述缺点,于是产生了工厂方法模式



03


工厂方法模式



工厂方法模式定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法是一个类的实例化延迟到其子类。

UML 如图

8分钟帮你理清工厂模式
工厂方法模式UML

工厂方法模式包含 4 个角色

Product :抽象产品,定义工厂方法所创建的对象的接口,也就是实际需要使用的对象的接口

ConcreteProduct :具体产品,具体的 Product 接口的实现对象

Factory :工厂接口,申明工厂方法,通常返回一个 Product 类型的实例对象

ConcreteFactory:工厂实现,覆盖 Factory 定义的工厂方法,返回具体的 Product 实例

此处小K在简单工厂模式的代码基础上稍作改动,增加工厂方法接口,同时该接口中包含一个 createPhone 抽象方法,返回值为 MobilePhone

public  interface FactoryMethod {
MobilePhone createPhone();
}

然后定义三个具体工厂类型 HuaWeiFactoryAppleFactoryXiaoMiFactory,分别用于创建华为、苹果、小米手机,具体代码如下

// 华为手机工厂
public class HuaWeiFactory implements FactoryMethod{
@Override
public MobilePhone createPhone() {
return new HuaWei();
}
}
// 苹果手机工厂
public class AppleFactory implements FactoryMethod{
@Override
public MobilePhone createPhone() {
return new Apple();
}
}
// 小米手机工厂
public class XiaoMiFactory implements FactoryMethod{
@Override
public MobilePhone createPhone() {
return new XiaoMi();
}
}

这时想要生产某一产品,就需要先构建该产品的生产工厂

public static void main(String[] args) {
FactoryMethod concreteFactory;
// 华为工厂生产华为手机
concreteFactory= new HuaWeiFactory();
MobilePhone huaWei = concreteFactory.createPhone();
huaWei.makeCall();
// 小米工厂只生产小米手机
concreteFactory= new XiaoMiFactory();
MobilePhone xiaoMi = concreteFactory.createPhone();
xiaoMi.makeCall();
}

工厂方法模式的优缺点

优点

  • 一个调用者想创建一个对象,只要知道其名称就可以了。
  • 扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
  • 屏蔽产品的具体实现,调用者只关心产品的接口。

缺点

  • 每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。

工厂方法模式和简单工厂的区别主要有两点

  • 工厂方法需要定义抽象工厂类或接口,例如上述的 MobileFactory
  • 具体的对象由具体的工厂类(如 HuaWeiFactory)来创建,并且每个工厂只创建一类对象。



04


抽象工厂模式



抽象工厂模式为创建一组相关或相互依赖的对象提供一个接口,而且无需指定它们的具体类

UML 如图

8分钟帮你理清工厂模式
抽象工厂模式UML

抽象工厂模式主要包含四个角色

抽象工厂模式包含的角色(要素):

AbstractFactory:抽象工厂,用于声明生成抽象产品的方法

ConcreteFactory:具体工厂,实现抽象工厂定义的方法,具体实现一系列产品对象的创建

AbstractProduct:抽象产品,定义一类产品对象的接口

ConcreteProduct:具体产品,通常在具体工厂里,会选择具体的产品实现,来创建符合抽象工厂定义的方法返回的产品类型的对象。

抽象工厂和工厂方法相似,但是其需要对每一个产品功能进行抽象

工厂类如下

public  interface AbstractFactory {
MakeCall makeCall();
SurfInternet surfInternet();
}
public class AppleFactory implements AbstractFactory{
@Override
public MakeCall makeCall() { return new AppleMakeCall(); }
@Override
public SurfInternet surfInternet() { return new AppleSurfInternet(); }
}
public class HuaWeiFactory implements AbstractFactory{
@Override
public MakeCall makeCall() { return new HuaWeiMakeCall(); }
@Override
public SurfInternet surfInternet() { return new HuaWeiSurfInternet(); }
}

产品功能类如下

// 通话功能
public interface MakeCall {
void communicate();
}
public class AppleMakeCall implements MakeCall{
@Override
public void communicate() {
System.out.println("苹果正在通话");
}
}
public class HuaWeiMakeCall implements MakeCall{
@Override
public void communicate() {
System.out.println("华为正在通话");
}
}
// 上网功能
public interface SurfInternet {
void online();
}
public class AppleSurfInternet implements SurfInternet{
@Override
public void online() {
System.out.println("苹果正在上网");
}
}
public class HuaWeiSurfInternet implements SurfInternet{
@Override
public void online() {
System.out.println("华为正在上网");
}
}

使用时,需要针对每个产品及功能,进行实例化

public static void main(String[] args) {
AbstractFactory huaWeiFactory = new HuaWeiFactory();
MakeCall makeCall = huaWeiFactory.makeCall();
makeCall.communicate(); //华为专用通话

AbstractFactory appleFactory = new AppleFactory();
SurfInternet surfInternet = appleFactory.surfInternet();
surfInternet.online(); // 苹果专用上网
}

可以看到,抽象工厂实际上就是工厂方法的升级版,它的一个工厂类能够生产多个不同类型的、相关联或者相互依赖的对象。

抽象工厂与简单工厂也很类似,只是简单工厂通过一个静态函数创建不同类型的对象,而抽象工厂通过工厂子类的多个函数创建多个不同类型的对象

抽象工厂模式的优缺点

优点

  • 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易,所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。
  • 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。
  • 增加新的产品族很方便,无须修改已有系统,符合“开闭原则”。

缺点

  • 增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,违背了“开闭原则”。

  • 开闭原则:软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的



05


适用场景


不管是简单工厂模式,工厂方法模式还是抽象工厂模式,他们具有类似的特性,所以他们的适用场景也是类似的。

  • 首先,作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过new就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。

  • 其次,工厂模式是一种典型的解耦模式,迪米特法则在工厂模式中表现的尤为明显。假如调用者自己组装产品需要增加依赖关系时,可以考虑使用工厂模式。将会大大降低对象之间的耦合度。

  • 再次,由于工厂模式是依靠抽象架构的,它把实例化产品的任务交由实现类完成,扩展性比较好。也就是说,当需要系统有比较好的扩展性时,可以考虑工厂模式,不同的产品用不同的实现工厂来组装。

工厂模式在我们这些Java码农身边可以说无处不在。

小到JDK中的 calendar

Calendar calendar = Calendar.getInstance();

还有常用的日志框架slf4J

private  final  static Logger logger = LoggerFactory.getLogger(HelloWord.class);

当然,最为猿所乐道的就是 Spring 中的 BeanIOC 通过 FactoryBeanBean进行管理,想了解更多关于Bean的内容,可以找下栗子为《一文搞懂bean的生命周期》

说来Bean可以由反射和单例模式进行构建,下期要不要讲这个呢?



06


总结



本文主要介绍了面试常遇到的设计模式问题,详细的比对了工厂模式的三个子类:简单工厂、工厂方法和抽象工厂。既有UML图,又有具体实现,还包括优缺点分析,感兴趣的小伙伴可以仔细阅读一下哟

这里是花栗鼠小K,下次有🌰,我再来,拜拜~~~

关注六只栗子,面试不迷路


作者    花栗鼠小K

编辑   一口栗子  


原文始发于微信公众号(六只栗子):8分钟帮你理清工厂模式

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/88648.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!