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

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

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


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");
    }
}

定义接口和实现

如上图所示:一个接口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");
    }
}

接口与实现如下图所示

一个接口返回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之路。公众号内有大量的技术文章、海量视频资源、精美脑图,不妨来关注一下!回复【资料】领取大量学习资源和免费书籍!

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值