11

设计模式系列| 工厂方法模式

 3 years ago
source link: https://mp.weixin.qq.com/s?__biz=Mzg5MjQ5MzY2Mg%3D%3D&%3Bmid=2247485715&%3Bidx=1&%3Bsn=0ac3c88dc0f8b75401ff72f8e7d713bd
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

大家好,我是狼王,一个爱打球的程序员

这是设计模式的第二篇,我们都知道 「工厂模式」 有三种, 「简单工厂,工厂方法,抽象工厂」 ,这篇让我们来认识一下 「工厂模式」

1、概述

工厂方法模式是一种创建型设计模式, 其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。

2、适用场景

1)不确定对象的类别、个数或者依赖关系。 2)需要扩展工具库或者内部组件。 3)需要重复使用的对象(例如资源池等)。

3、实例

有以下业务场景:一个商店,出售多种货物,包括汽车car,船ship,飞机plane。

3.1 不使用工厂模式

定义三个实体类

import lombok.Data;

/**
* Car
*/

@Data
public class Car {

private String name;

private String price;

public void run() {
System.out.println("the car is running");
}
}
import lombok.Data;

/**
* Plane
*/

@Data
public class Plane {

private String name;

private String price;

public void run() {
System.out.println("the Plane is running");
}
}
import lombok.Data;

/**
* Ship
*/

@Data
public class Ship {

private String name;

private String price;

public void run() {
System.out.println("the Ship is running");
}
}

定义接口和实现

3emEjuQ.jpg!mobile

如上图所示:一个接口IShop,有一个实现类ShopImpl,获取三种产品的方法在这个接口中定义了三次。

/**
* IShop
*/

public interface IShop {

/**
* 获取汽车
*/

Car getCar();

/**
* 获取船
*/

Ship getShip();

/**
* 获取飞机
*/

Plane getPlane();
}
import com.cloud.bssp.designpatterns.factorymethod.withoutdesign.Car;
import com.cloud.bssp.designpatterns.factorymethod.withoutdesign.IShop;
import com.cloud.bssp.designpatterns.factorymethod.withoutdesign.Plane;
import com.cloud.bssp.designpatterns.factorymethod.withoutdesign.Ship;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
* ShopImpl
*/

@Slf4j
@Service
public class ShopImpl implements IShop {
@Override
public Car getCar() {
log.info("this is car");
return new Car();
}

@Override
public Ship getShip() {
log.info("this is Ship");
return new Ship();
}

@Override
public Plane getPlane() {
log.info("this is Plane");
return new Plane();
}
}

创建一个测试类


/**
* test
*/

@RunWith(SpringRunner.class)
@SpringBootTest(classes
= BsspUserApplication.class)
public class TestDemo
{

@Autowired
private IShop shop;

@Test
public void testWithout() {

Car car = shop.getCar();
car.run();

Ship ship = shop.getShip();
ship.run();

Plane plane = shop.getPlane();
plane.run();

}
}

执行结果

2021-01-10 14:54:24.078  INFO 16884 --- [           main] c.c.b.d.f.withoutdesign.impl.ShopImpl    : this is car
the car is running
2021-01-10 14:54:24.078 INFO 16884 --- [ main] c.c.b.d.f.withoutdesign.impl.ShopImpl : this is Ship
the Ship is running
2021-01-10 14:54:24.078 INFO 16884 --- [ main] c.c.b.d.f.withoutdesign.impl.ShopImpl : this is Plane
the Plane is running

3.2 使用工厂模式

创建一个Product接口,定义run()方法

/**
* Product
*/

public interface Product {

/**
* 行驶
*/

void run();
}

定义三个实体类

import lombok.Data;

/**
* Car
*/

@Data
public class Car implements Product{

private String name;

private String price;

@Override
public void run() {
System.out.println("the car is running");
}
}
import lombok.Data;

/**
* Plane
*/

@Data
public class Plane implements Product{

private String name;

private String price;

@Override
public void run() {
System.out.println("the Plane is running");
}
}
import lombok.Data;

/**
* Ship
*/

@Data
public class Ship implements Product{

private String name;

private String price;

@Override
public void run() {
System.out.println("the Ship is running");
}
}

接口与实现如下图所示

JZbE7r3.jpg!mobile

一个接口返回Product,三个实现分别返回三个产品实体。

定义一个工厂方法接口

/**
* IShop
*/

public interface IShop {

/**
* 获取商品
*/

Product getProduct();
}

定义工厂接口实现

/**
* Ship
*/

@Data
public class Ship implements Product{

private String name;

private String price;

@Override
public void run() {
System.out.println("the Ship is running");
}
}
/**
* Plane
*/

@Data
public class Plane implements Product{

private String name;

private String price;

@Override
public void run() {
System.out.println("the Plane is running");
}
}
import lombok.Data;

/**
* Car
*/

@Data
public class Car implements Product{

private String name;

private String price;

@Override
public void run() {
System.out.println("the car is running");
}
}

定义一个测试类


/**
* test
*/

@RunWith(SpringRunner.class)
@SpringBootTest(classes
= BsspUserApplication.class)
public class TestDemo
{

@Autowired
private IShop shipImpl;

@Autowired
private IShop carImpl;

@Autowired
private IShop planeImpl;

@Test
public void testUsed() {
//以下使用注入的方式,注意在工厂IShop的每个实现指定bean的名称
Product car = carImpl.getProduct();

Product ship = shipImpl.getProduct();

Product plane = planeImpl.getProduct();

//以下使用new的方式
// IShop planeImpl = new PlaneImpl();
// Product plane = planeImpl.getProduct();
//
//
// IShop carImpl = new CarImpl();
// Product car = carImpl.getProduct();
// car.run();
//
// IShop shipImpl = new ShipImpl();
// Product ship = shipImpl.getProduct();

car.run();
plane.run();
ship.run();
}
}

运行结果

2021-01-10 15:01:59.394  INFO 21132 --- [           main] c.c.b.d.f.usedesign.impl.CarImpl         : this is car
2021-01-10 15:01:59.394 INFO 21132 --- [ main] c.c.b.d.f.usedesign.impl.ShipImpl : this is Ship
2021-01-10 15:01:59.394 INFO 21132 --- [ main] c.c.b.d.f.usedesign.impl.PlaneImpl : this is plane
the car is running
the Plane is running
the Ship is running

4、分析

分析对比以上两种实现方式:

「使用工厂发的实体类」,定义了一个接口,其内部可以定义一些通用的方法,实体类可以通过实现该接口重写该方法,方法名统一。

「在没使用工厂的实体类中」,虽然也可以自定义方法,但是没有对方法的名称有限制,可自定义。后续实体越来越多,可能通用的方法名会起的多种多样,造成代码混乱,不利于统一。

「使用工厂方式」,提供一个统一接口,内部提供一个统一获取产品的方法,其具体返回那种产品由其实现方法进行指定。此接口永远不会被修改,只会在其实现类去扩展。

「未使用的工厂方法」的同样提供一个接口,但是其内部分别提供了获取三种产品的三个方法,后面随着产品越来越多,这个接口的方法也会越来越多。

「使用工厂方法」的针对不同的产品分别实现了各个产品的实现类,每个实现类返回对应的产品,相互之间没有任何耦合。

「未使用工厂方法」的在一个实现类内有三种产品的获取方法,随着产品种类增加,此类会越来越长,容易造成代码耦合和过长,不利于代码阅读和管理,扩展性很差,每次新增都需要修改这个实现类。

5、总结

最后总结下上面例子中使用工厂方法的优缺点:

「1)符合开闭原则,对扩展开放,对修改关闭。」

「2)符合迪米特原则,类与类之间没有关联,降低耦合。」

「3)符合单一职责,一个类或方法只负责一件事。」

「引入了很多的子类,代码变得复杂。」

乐于输出 干货 的Java技术公众号:Garnett的Java之路。公众号内有大量的技术文章、海量视频资源、精美脑图,不妨来 关注 一下! 回复【资料】领取大量学习资源和免费书籍!

niyeIvJ.jpg!mobile

转发朋友圈是对我最大的支持!

觉得有点东西就点一下“赞和在看”吧!感谢大家的支持了!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK