Java设计模式:工厂模式(简单工厂模式、工厂方法模式、抽象工厂模式)
? 作者主页:欢迎来到我的技术博客😎
? 个人介绍:大家好,本人热衷于Java后端开发,欢迎来交流学习哦!( ̄▽ ̄)~*
🍊 如果文章对您有帮助,记得关注、点赞、收藏、评论??????
📣 您的支持将是我创作的动力,让我们一起加油进步吧!!!🎉🎉
文章目录
一、为什么需要工厂模式
在软件开发中,创建对象实例最常用的方式就是通过 new
操作符直接生成对象,但是有时候需要根据不同的条件来创建不同类型的对象,而直接在代码中使用 new
关键字创建对象会导致代码的耦合度增加,不利于系统的维护和扩展。在这种情况中,创建对象不仅仅只是一个 操作,而是有一个复杂的 过程。
因此,对于出现的这种的情况,我们怎么做到就是轻松方便地构造对象实例,而不用去关心对象实例的细节以及创建中的复杂流程呢?这时候我们引入 工厂类 来负责对象的创建。
二、什么是工厂模式
在工厂模式中,通常会定义一个 抽象工厂接口,该接口声明了创建对象的方法,具体的工厂类 实现了这个接口,并负责实际 创建对象的过程。客户端代码通过使用工厂接口来创建对象,而不直接使用 new
关键字实例化对象,从而实现 对象创建和使用的解耦。
工厂模式可以分为三类:
- 简单工厂模式(Simple Factory)
- 工厂方法模式(Factory Method)
- 抽象工厂模式(Abstract Factory)
这三种模式从上到下逐步抽象,并且更具一般性。在《设计模式》这本书中将工厂模式分为两类:工厂方法模式和抽象工厂模式,将简单工厂模式看作是工厂方法模式中的一种特例,在实际开发中,简单工厂模式反而更像是一种 编程习惯。
三、未使用工厂模式的案例
在引入简单工厂模式之前,我们先来演示一下没有使用工厂模式的业务场景。
在没有使用工厂模式的情况下,当客户前往手机店购买苹果手机时,手机店必须亲自进行苹果手机的制造工作;同样,如果客户想购买小米手机,手机店也必须自行进行小米手机的制造工作。
类图如下:
具体的类设计如下:
手机抽象类:
public abstract class Phone {
public abstract String getName();
//外观定制
public void addAppearance() {
System.out.println("外观定制");
}
//硬件配置
public void addConfiguration() {
System.out.println("硬件配置");
}
}
苹果手机类:
public class ApplePhone extends Phone {
@Override
public String getName() {
return "苹果手机";
}
}
小米手机类:
public class XiaomiPhone extends Phone {
@Override
public String getName() {
return "小米手机";
}
}
手机店类:
public class PhoneStore {
public Phone orderPhone(String type) {
//声明Phone类型的变量,根据不同类型创建不同的phone子类对象
Phone phone = null;
if ("Apple".equals(type)) {
phone = new ApplePhone();
} else if ("Xiaomi".equals(type)) {
phone = new XiaomiPhone();
} else {
throw new RuntimeException("暂没有该手机型号");
}
//配置
phone.addAppearance();
phone.addConfiguration();
return phone;
}
}
客户端类:
public class Client {
public static void main(String[] args) {
//1. 创建手机店类
PhoneStore store = new PhoneStore();
//2. 购买手机
Phone phone = store.orderPhone("Apple");
System.out.println(phone.getName());
}
}
至此,客户就可以在客户端中购买所需要什么品牌的手机,然后由手机店自己去制造手机后交付于客户。
说明:
在上述代码中 Phone
类被声明为抽象类,在抽象类中声明了一个抽象方法 getName()
,但是没有具体的实现方法,而具体的实现留给子类去完成。其他两个方法是具体的实现方法,子类继承直接默认获得这两个功能,不需要再去实现。
这时候我们可以发现,这不就和接口特别相似,但是两者却有着不同之处,接下来我们讲一下 抽象类 和 接口 区别:
抽象类:
- 可以包含抽象方法和具体方法;
- 可以包含字段(成员变量);
- 不能被实例化,需要通过继承来使用;
- 一个类只能继承一个抽象类;
接口:
- 只能包含抽象方法;
- 不能包含字段(成员变量);
- 所有方法默认为
public
,所有字段默认为public, static, final
; - 可以被多个类继承(支持多重继承);
四、简单工厂模式
通过上面的案例,我们看到手机店不仅要卖手机还要自己去制造手机。因此我们引入了工厂模式,我们可以使用简单工厂模式来创建一个工厂类,由工厂类去实现手机的制造,而用户只需要在客户端传入工厂类的参数,工厂类就会去制造相对应的手机出来,手机店只需去卖给客户需要的手机即可,不需要关心手机是如何被制造出来的整个过程。
1. 简单工厂模式的定义
简单工厂模式的核心是定义一个创建对象的接口,将对象的创建和本身的业务逻辑分离,降低系统的耦合度,当需要改变的时候,只需要修改工厂类即可。
2. 简单工厂模式的结构
简单工厂模式包含以下的角色:
- 抽象产品 :定义了产品的规范,描述了产品的主要特性和功能。
- 具体产品 :实现或者继承抽象产品的子类
- 具体工厂 :提供了创建产品的方法,调用者通过该方法来获取产品。
3. 简单工厂模式的实现
使用简单工厂模式对上面案例进行改造,类图如下:
?
具体的类设计如下:
对于手机抽象类、苹果手机类、小米手机类、客户端这几个类我们都不需要进行修改,我们只需要修改手机店类以及创建一个工厂类即可。
工厂类:
public class SimplePhoneFactory {
public Phone createPhone(String type) {
//声明Phone类型的变量,根据不同类型创建不同的phone子类对象
Phone phone = null;
if ("Apple".equals(type)) {
phone = new ApplePhone();
} else if ("Xiaomi".equals(type)) {
phone = new XiaomiPhone();
} else {
throw new RuntimeException("暂没有该手机型号");
}
//配置
phone.addAppearance();
phone.addConfiguration();
return phone;
}
}
手机店类:
public class PhoneStore {
public Phone orderPhone(String type) {
//创建工厂类对象
SimplePhoneFactory factory = new SimplePhoneFactory();
//调用制造手机的方法
Phone phone = factory.createPhone(type);
//配置
phone.addAppearance();
phone.addConfiguration();
return phone;
}
}
4. 简单工厂模式的优缺点
优点:
简单工厂模式封装了创建对象的过程,可以通过参数直接获取对象。把对象的创建和业务逻辑层分开,这样以后就避免了修改客户代码,如果要实现新产品直接修改工厂类,而不需要在原代码中修改,这样就降低了客户代码修改的可能性,更加容易扩展。
缺点:
每次添加新产品就需要修改工厂类,不符合 “开闭原则”。在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展维护,并且工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。
对上面提到的 开闭原则 进行讲解,开闭原则(Open/Closed Principle,OCP)是面向对象设计中一个重要原则,它包含了两个核心概念:
- 对扩展开发: 意味着在不修改现有代码的情况下,可以通过添加新的功能来扩展系统的功能。新功能的添加应该是通过添加新的类、模块、接口等方式进行的,而不是通过修改已有的代码。
- 对修改关闭: 意味着已经存在的代码不应该被修改,因为修改可能引入错误,破坏原有的稳定性。系统的行为可以通过扩展来改变,但不应该通过修改已有的代码来实现。
五、工厂方法模式
1. 工厂方法模式的定义
针对上述简单工厂模式中的缺点,使用 工厂方法模式 就可以完美解决,完全遵循开闭原则。
工厂方法模式将工厂抽象化,并定义一个创建对象的接口。每增加新产品时,只需增加该 产品类 以及该产品对应的 具体实现工厂类,由具体工厂类决定要实例化的产品是哪个,将对象的创建与实例化延迟到子类,这样工厂的设计就符合“开闭原则”了,扩展时不必去修改原来的代码。
2. 工厂方法模式的结构
工厂方法模式包含以下的角色:
- 抽象工厂 : 提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
- 具体工厂 : 主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
- 抽象产品 : 定义了产品的规范,描述了产品的主要特性和功能。
- 具体产品 : 实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一 一对应。
3. 工厂方法模式的实现
使用工厂方法模式对上面案例进行改造,类图如下:
?
具体的类设计如下:
对于手机抽象类、苹果手机类、小米手机类这几个类不需要修改,而手机店类和客户端类需要修改,并且新增抽象工厂(手机工厂)、具体工厂(苹果手机工厂、小米手机工厂)。
手机工厂:
public interface PhoneFactory {
Phone createPhone();
}
苹果手机工厂:
public class ApplePhoneFactory implements PhoneFactory {
@Override
public Phone createPhone() {
return new ApplePhone();
}
}
小米手机工厂:
public class XiaomiPhoneFactory implements PhoneFactory {
@Override
public Phone createPhone() {
return new XiaomiPhone();
}
}
手机店类:
public class PhoneStore {
private PhoneFactory factory;
public void setFactory(PhoneFactory factory) {
this.factory= factory;
}
public Phone orderPhone() {
Phone phone = factory.createPhone();
//配置
phone.addAppearance();
phone.addConfiguration();
return phone;
}
}
客户端类:
public class Client {
public static void main(String[] args) {
//创建手机店对象
PhoneStore store = new PhoneStore();
//创建对象
// ApplePhoneFactory factory = new ApplePhoneFactory();
XiaomiPhoneFactory factory = new XiaomiPhoneFactory();
store.setFactory(factory);
//购买手机
Phone phone = store.orderPhone();
System.out.println(phone.getName());
}
}
4. 工厂方法模式的优缺点
优点:
- 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程;
- 在系统增加新的产品时只需要添加 具体产品类 和 对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则;
缺点:
- 每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。
六、抽象工厂模式
首先,我们上述案例再新增点需求,比如我们还要生产苹果手表和小米手表等产品,按照工厂方法模式的话我们需要去创建每个产品对应的产品类以及对应的具体工厂类,如果新增的产品很多的话,那么很容易发生类爆炸情况。
我们试想一下可不可以这样实现,就是属于同一品牌的产品可以由同一工厂类去创建,比如创建一个苹果工厂类,它就可以同时生产出苹果手机和苹果手表等都属于苹果品牌的产品出来。因此,我们就引入了 抽象工厂模式。
1. 抽象工厂模式的定义
定义:抽象工厂模式是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到 同族的不同等级的产品 的模式结构。
抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产 一个等级 的产品,而抽象工厂模式可生产 多个等级 的产品。
?
使用抽象工厂模式一般要满足以下条件:
- 系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品;
- 系统一次只能消费其中某一族产品,即同族的产品一起使用;
在介绍抽象工厂模式前,我们先理解一下产品等级和产品族这两个在软件设计中经常出现的概念:
产品等级
- 产品等级指的是具有共同特性的产品集合,它们通常是在某个方面上具有共性或者属于同一类别的产品。
- 例如,在汽车制造中,轿车、卡车和摩托车都可以构成不同等级的产品。它们都是交通工具,但在功能、结构和用途上有所不同。
产品族
- 产品族是指不同等级产品中相关联的一组产品集合,它们之间可能存在某种内在的联系或者依赖关系。
- 比如,在汽车制造中,汽车可能有多种不同型号,每种型号都有不同的配置和特点,比如轿车产品族可能包括轿车、跑车、SUV等不同类型。
为了方便理解,我们看一下产品等级和产品族的示意图:
2. 抽象工厂模式的结构
抽象工厂模式包含以下的角色:
- 抽象工厂 : 提供了创建产品的接口,它包含多个创建产品的方法,可以创建多个不同等级的产品。
- 具体工厂 :主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
- 抽象产品 : 定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
- 具体产品 : 实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。
3. 抽象工厂模式的实现
使用抽象工厂模式对上面的案例进行改造,类图如下:
?
具体的类设计如下:
对于手机抽象类、苹果手机类、小米手机类这几个类不需要修改,而客户端类、抽象工厂类需要修改,并且新增手表抽象类、苹果手表类、小米手表类、苹果工厂类、小米工厂类。
手表抽象类:
public abstract class Watch {
public abstract String getName();
}
苹果手表类:
public class AppleWatch extends Watch {
@Override
public String getName() {
return "苹果手表";
}
}
小米手表类:
public class XiaomiWatch extends Watch {
@Override
public String getName() {
return "小米手表";
}
}
抽象工厂类:
public interface PhoneAndWatchFactory {
//生产手机的功能
Phone createPhone();
//生产手表的功能
Watch createWatch();
}
苹果工厂类:
public class AppleFactory implements PhoneAndWatchFactory {
@Override
public Phone createPhone() {
return new ApplePhone();
}
@Override
public Watch createWatch() {
return new AppleWatch();
}
}
小米工厂类:
public class XiaomiFactory implements PhoneAndWatchFactory {
@Override
public Phone createPhone() {
return new XiaomiPhone();
}
@Override
public Watch createWatch() {
return new XiaomiWatch();
}
}
客户端类:
public class Client {
public static void main(String[] args) {
//创建的是苹果工厂对象
AppleFactory factory = new AppleFactory();
//获取苹果手机和苹果手表
Phone phone = factory.createPhone();
Watch watch = factory.createWatch();
System.out.println(phone.getName());
System.out.println(watch.getName());
}
}
4. 抽象工厂模式的优缺点
优点:
- 抽象工厂模式能够确保创建的产品族是相互关联、一致的。这意味着通过抽象工厂创建的对象能够相互配合使用,形成一个完整的系统。
- 由于客户端通过抽象接口使用产品,因此更换产品系列相对容易。只需要更改具体工厂的实例,而不需要修改客户端代码。
- 新增一个产品族的实现,只需扩展抽象工厂和具体工厂,而不需要修改已有代码,符合开闭原则。
缺点:
- 当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。
七、工厂模式总结
简单工厂模式: 让一个工厂类负责创建所有对象;但没有考虑后期扩展和维护,修改违背开闭原则,静态方法不能被继承。
工厂方法模式: 主要思想是继承,修改符合开闭原则;但每个工厂只能创建一种类型的产品。
抽象工厂模式: 主要思想是组合,本质是产品族,实际包含了很多工厂方法,修改符合开闭原则;但只适用于增加同类工厂这种横向扩展需求,不适合新增功能方法这种纵向扩展。
其实这三种工厂模式在形式和特点上都非常相似,甚至存在一定的内在联系,而且最终目的都是解耦。在使用时,我们不必去在意这个模式到底工厂方法模式还是抽象工厂模式,因为它们之间也是可以灵活转变的。
?
非常感谢您阅读到这里,如果这篇文章对您有帮助,希望能留下您的点赞👍 关注💖 分享👥 留言💬thanks!!!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!