工厂设计模式
实现了创建者和调用者分离。
分类:
简单工厂模式
工厂方法模式
抽象工厂模式
核心本质:
① 实例化对象不使用new,用工厂方法代替。
② 将选择实现类,创建对象统一管理和控制。从而将调用者跟我们的实现类解耦。
简单工厂模式
再看简单工厂模式之前,我们先来看一看我们使用传统的方式创建对象是怎么来创建的。
首先我们有一个Car接口,其实现类有Audi、BMW、Benz。我们想要这三个实现类的对象时,我们就需要使用new关键字。即:
// 给定一个Car接口
public interface Car {
void name();
}
//Audi实现了Car接口,使其具有车的属性
public class Audi implements Car{
@Override
public void name() {
System.out.println("奥迪");
}
}
//同理Benz
public class Benz implements Car {
@Override
public void name() {
System.out.println("奔驰");
}
}
//同理BMW
public class BMW implements Car {
@Override
public void name() {
System.out.println("宝马");
}
}
此时,有一个消费者,他想要买一辆车,这个时候,我们传统的方式是去new一个车,即:
public class Consumer {
public static void main(String[] args) {
Car car1 = new Audi();
Car car2 = new BMW();
Car car3 = new Benz();
car1.name();
car2.name();
car3.name();
}
}
图示:
这种传统的方式看起来无可厚非,我们想要什么车,就去new什么车就好了,但是,这种传统的方式相当于我们自己去"造车",如果我们调用Audi类的构造器去new时,这个构造器有多个参数,那么我们每去new一个实例时,都要往里面传多个参数。这样就会很麻烦。有没有一种方式,我们不用去关注如何创建的这个对象,就可以拿到这个对象呢?下来我们来看一看简单工厂方式。
简单工厂模式,我们使用一个工厂类,这个工厂类帮助我们去创建我们想要的车的类型,我们只需要传入我们想要车的名字即可。
该工厂类定义如下:
public class CarFactory {
public static Car getCar(String name){
if (name.equals("奥迪")){
return new Audi();
}else if(name.equals("奔驰")){
return new Benz();
}else if(name.equals("宝马")){
return new BMW();
}else{
return null;
}
}
}
我们看到,这个工厂根据我们传入的参数,可以创建出对应类的实例。此时,我们想要拿到某一个类的实例时,只需要传入对应的参数即可:
public class Consumer {
public static void main(String[] args) {
Car car1 = CarFactory.getCar("奥迪");
Car car2 = CarFactory.getCar("奔驰");
Car car3 = CarFactory.getCar("宝马");
car1.name();
car2.name();
car3.name();
}
}
我们并没有自己去"造车",而是将造车的任务交给了CarFactory这个类,对于怎么造的车,对于我们消费者来说是不知道的,消费者只需要输入自己想要的车就行了。
但是,这种方式也有缺点,我们是可以不用关心如何创建的实例,但是,如果我们想要一个新的类的实例时,就必须改动我们的工厂类的代码,这违反了我们的开闭原则。还是用这个例子举例,有一天突然来了个消费者,它不想要Audi,也不想要Benz,也不想要BMW,他就支持国产,他想买一辆哈佛,此时,我们就必须修改CarFactory的代码,并提供Haval类。(修改CarFactory代码违反了开闭原则)
public class Haval implements Car {
@Override
public void name() {
System.out.println("哈佛");
}
}
public class CarFactory {
public static Car getCar(String name){
if (name.equals("奥迪")){
return new Audi();
}else if(name.equals("奔驰")){
return new Benz();
}else if(name.equals("宝马")){
return new BMW();
}else if (name.equals("哈佛")){
return new Haval();
}else{
return null;
}
}
}
有没有一种形式,我们无需修改代码呢?接下来我们介绍工厂模式
工厂方法模式
在前面,我们说到了简单工厂模式,也说到了它的缺点,而我们的工厂模式,则是简单工厂模式的改进版本。
工厂模式中包含的角色:
① 抽象工厂(Factory):在抽象工厂类中,声明了工厂方法,用于返回一个产品。所有创建对象的工厂类都必须实现该接口。
② 具体工厂(ConcreteFactory):它是抽象工厂的子类,实现了抽象工厂中定义的工厂方法,并可由客户端调用,返回一个具体的产品。
③ 抽象产品(Product):它是定义产品的接口,是工厂方法模式所创建对象的超类型,也就是所有产品的公共父类。
④ 具体产品(ConcreteProduct):它实现了抽象产品接口,某种类型的具体产品由专门的具体工厂创建,具体工厂和具体产品之间是 一 一 对应的关系。
我们可以为每一个类型的车,都创建一个车工厂,即奥迪有自己的车间,奔驰有自己的车间等等。于是,我们将所有品牌的车的车间抽象出来,创建一个CarFactory接口,每个品牌的车都实现了CarFactory接口,表示其具有造车的功能:
// 车间
public interface CarFactory {
Car getCar();
}
//奥迪车间
public class AudiFactory implements CarFactory {
@Override
public Car getCar() {
return new Audi();
}
}
//奔驰车间
public class BenzFactory implements CarFactory {
@Override
public Car getCar() {
return new Benz();
}
}
//宝马车间
public class BMWFactory implements CarFactory {
@Override
public Car getCar() {
return new BMW();
}
}
//消费者
public class Consumer {
public static void main(String[] args) {
Car car1 = new BMWFactory().getCar();
Car car2 = new AudiFactory().getCar();
Car car3 = new BenzFactory().getCar();
}
}
相比较简单工厂模式来说,简单工厂是一个人去了一家工厂,这家工厂里面只有奥迪、奔驰、宝马这三种车,这个人只能在这三种车里面选,如果想选其他的车,这家工厂就得进行改进,去创建新的车。
而工厂方法模式就好比,一个人去买车,我想买奥迪,我就去奥迪4S店,我想买奔驰,我就去奔驰4S店,我想买宝马,我就去宝马4S店,我想买哈佛,我就去哈佛4S店。这个时候就会有疑问了啊,这里明显没有HavalFactory,怎么去买哈佛啊?我们可以新创建一个HavalFactory呀,这里就是和简单工厂区分的一个点,简单工厂模式如果想要添加新的功能,就必须修改工厂类里的代码,但是工厂方法模式不需要修改,注意我这里说的是修改原工厂类,在工厂方法模式中,我即使添加了一个HavalFactory和Haval类,我都没有修改原有工厂类里的代码。
细心地朋友可能注意到了,嗷,虽然你没有修改原有工厂类的代码,但是,你每多一个车,你就会多一个扩展类,这样非常麻烦。
抽象工厂模式
正在工厂方法模式中,具体工厂负责生产具体的产品,每一个具体工厂对应一个具体产品,工厂方法具有唯一性。有时候,我们希望一个工厂可以提供多个产品对象。而不是单一的产品对象,如一个华为工厂,它可以生产华为手环、华为手机、华为路由器等多种设备,而不是只生产某一种设备。为了更好地理解抽象工厂模式,我们先引入两个概念:
产品等级结构:产品等级即产品的继承结构,如一个抽象类是手机,其子类有华为手机,小米手机,苹果手机。则抽象类与具体品牌的手机之间构成了一个产品等级结构。
产品族:在抽象工厂模式中,产品族指由一个工厂生产的,位于不同产品等级结构中的一组产品。如华为工厂生产了华为手机、华为路由器、华为手环,这些都是由华为工厂生产的,华为手机位于手机产品等级结构中,华为手环位于手环产品等级结构中,华为路由器位于路由器产品等级结构中。
当系统所提供的工厂生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构、属于不同类型的具体产品时就可以使用抽象工厂模式。
抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。抽象工厂模式又称为Kit模式,是一种创建型模式。
抽象工厂模式中包含几个角色:
AbstractFactory(抽象工厂):它声明了一组用于创建一族产品的方法,每个方法对应一种产品。
ConcreteFactory(具体工厂):它实现了抽象工厂中声明的创建产品的方法,生产一组具有体产品,这些产品构成了一个产品族,每一个产品都位于不同的产品等级结构中
AbstractProduct(抽象产品):它为每种产品声明接口,在抽象产品中声明了产品所具有的业务方法。
ConcreteProduct(具体产品):它定义了具体工厂生产的具体产品的对象,实现抽象产品接口中声明的方法。
我们使用抽象工厂模式来构建产品的设计,其结构如下:
其中Factory接口充当抽象工厂,其子类HuaWeiFactory、XiaoMiFactory充当具体工厂。接口PhoneProduct、RouterProduct充当抽象产品,其子类HuaWeiPhone、XiaoMiPhone、HuaWeiRouter、XiaoMiRouter充当具体产品。代码如下:
// 抽象工厂
public interface ProductFactory {
// 生产手机
IphoneProduct phoneProduct();
// 生产路由器
RouterProduct routerProduct();
}
// 具体工厂:华为工厂
public class HuaWeiFactory implements ProductFactory {
@Override
public IphoneProduct phoneProduct() {
return new HuaWeiPhone();
}
@Override
public RouterProduct routerProduct() {
return new HuaWeiRouter();
}
}
// 具体工厂:小米工厂
public class XiaoMiFactory implements ProductFactory {
@Override
public IphoneProduct phoneProduct() {
return new XiaoMiPhone();
}
@Override
public RouterProduct routerProduct() {
return new XiaoMiRouter();
}
}
// 抽象产品:手机
public interface IphoneProduct {
void start();
void shutdown();
void call();
// 发短信
void sendMes();
}
// 具体手机产品:华为手机
public class HuaWeiPhone implements IphoneProduct {
@Override
public void start() {
System.out.println("华为开机");
}
@Override
public void shutdown() {
System.out.println("华为手机关机");
}
@Override
public void call() {
System.out.println("华为手机打电话");
}
@Override
public void sendMes() {
System.out.println("华为手机发短信");
}
}
//具体手机产品:小米手机
public class XiaoMiPhone implements IphoneProduct {
@Override
public void start() {
System.out.println("小米手机开机");
}
@Override
public void shutdown() {
System.out.println("小米手机关机");
}
@Override
public void call() {
System.out.println("小米手机打电话");
}
@Override
public void sendMes() {
System.out.println("小米手机发短信");
}
}
//抽象产品:路由器
public interface RouterProduct {
void start();
void shutdown();
void openwifi();
}
//具体路由器产品:小米路由器
public class XiaoMiRouter implements RouterProduct {
@Override
public void start() {
System.out.println("开启小米路由器");
}
@Override
public void shutdown() {
System.out.println("关闭小米路由器");
}
@Override
public void openwifi() {
System.out.println("开启小米wifi");
}
}
// 具体路由器产品:华为路由器
public class HuaWeiRouter implements RouterProduct {
@Override
public void start() {
System.out.println("开启华为路由器");
}
@Override
public void shutdown() {
System.out.println("关闭华为路由器");
}
@Override
public void openwifi() {
System.out.println("开启华为wifi");
}
}
//验证:
public class Client {
public static void main(String[] args) {
//小米系列产品
XiaoMiFactory xiaomifactory = new XiaoMiFactory();
System.out.println("========小米系列产品=======");
IphoneProduct xiaomiphone = xiaomifactory.phoneProduct();
xiaomiphone.start();
xiaomiphone.sendMes();
RouterProduct xiaomirouter = xiaomifactory.routerProduct();
xiaomirouter.start();
xiaomirouter.openwifi();
HuaWeiFactory huaweifactory = new HuaWeiFactory();
System.out.println("========华为系列产品=======");
IphoneProduct huaweiphone = huaweifactory.phoneProduct();
huaweiphone.start();
huaweiphone.sendMes();
RouterProduct huaweirouter = huaweifactory.routerProduct();
huaweirouter.start();
huaweirouter.openwifi();
}
}
其输出结果为:
抽象工厂模式的优点:
① 具体产品在应用层的代码隔离,无需关心创建的细节。
② 将一个系列的产品统一到一起创建。
抽象工厂模式的缺点:
① 如果我们想要新增加一个具体的产品,就必须修改抽象工厂里的代码。比如:我们想生产一个具体的产品:笔记本,我们就需要在抽象工厂里添加一个创建笔记本的方法,然后让具体工厂实现这个方法,显然这违反了“开闭原则”。