java设计模式-工厂模式

165 阅读8分钟

「这是我参与2022首次更文挑战的第31天,活动详情查看:2022首次更文挑战

模式定义:

定义一个用于创建对象的接口,让子类决定实例化哪一个类。FactoryMethod 使得一个类的实例化延迟到子类

image.png

简单工厂

假设我们有一个动物园,饲养员每天要喂养各种动物,但是小动物比较挑食,因此饲养员就需要记住每种动物喜欢吃什么食物,这样就可以有针对性的喂养啦,代码如下:

package com.jony.factory;

public class SimpleFactoryMethod {
    public static void main(String[] args) {
        //准备喂养小狗
        Animal dog=new Dog();
        dog.eat();
        //准备喂养小猫
        Animal cat=new Cat();
        cat.eat();
    }
}

interface Animal{
    public void eat();
}

class Dog implements Animal{
    @Override
    public void eat() {
        System.out.println("小狗爱啃骨头");
    }
}

class Cat implements Animal{
    @Override
    public void eat() {
        System.out.println("小猫爱吃鱼");
    }
}

好景不长,饲养员嫌工资低,辞职不干了,然后招聘了一个新的饲养员,但是这个饲养员对各种动物喜欢吃啥不知道,因此我们得帮他实现一个简单的方法,只要饲养员告诉我们啥动物,我们就告诉饲养员这个动物喜欢吃啥

package com.jony.factory;

public class SimpleFactoryMethod {
    public static void main(String[] args) {
        Animal animal=SimpleFactory.animalFactory("小狗");
        animal.eat();
    }
}

interface Animal{
    public void eat();
}

class Dog implements Animal{
    @Override
    public void eat() {
        System.out.println("小狗爱啃骨头");
    }
}

class Cat implements Animal{
    @Override
    public void eat() {
        System.out.println("小猫爱吃鱼");
    }
}

class SimpleFactory{
    public static Animal animalFactory(String animal){
        if("小狗".equals(animal)){
            return new Dog();
        }else if("小猫".equals(animal)){
            return new Cat();
        }else{
            return null;
        }
    }
}

执行结果如下:

image.png

这样饲养员就不用自己亲自记录各种小动物喜欢吃啥,直接告诉工厂准备喂养啥动物,然后就可以实现小动物喜欢吃啥了。

特点
1 它是一个具体的类,非接口 抽象类。有一个重要的create()方法,利用if或者 switch创建产品并返回。

2 create()方法通常是静态的,所以也称之为静态工厂。

缺点
1 扩展性差(想增加一种动物外,除了新增一个动物产品类,还需要修改工厂类方法)不符合开闭原则

2 不同的产品需要不同额外参数的时候不支持。

工厂方法模式

后期动物园发展壮大,又引入了一些飞行动物,现有的饲养员无法完成这么大工作量,因此就需要新的方案。

这样我们就可以创建两个工厂,小动物有小动物的工厂(食草动物),飞行动物有飞行动物的工厂(食肉动物)。

产品方面
Animal SmallAnimal FlyAnimal

interface Animals{
    public void eat();
}

class SmallAnimals implements Animals{
    @Override
    public void eat() {
        System.out.println("喜欢吃草");
    }
}

class FlyAnimal implements Animals{
    @Override
    public void eat() {
        System.out.println("喜欢吃肉");
    }
}

工厂:
Factory SmallFactory FlyFactory

interface Factory{
    public Animals getAnimal();
}

class SmallFactory implements Factory{

    @Override
    public Animals getAnimal() {
        return new SmallAnimals();
    }
}

class FlyFactory implements Factory{

    @Override
    public Animals getAnimal() {
        return new FlyAnimal();
    }
}

测试方法:

class Test{
    public static void main(String[] args) {
        SmallFactory smallFactory=new SmallFactory();
        smallFactory.getAnimal().eat();

        FlyFactory flyFactory=new FlyFactory();
        flyFactory.getAnimal().eat();
    }
}

根据例子可以描述为:一个抽象产品类,可以派生出多个具体产品类。一个抽象工厂类,可以派生出多个具体工厂类。每个具体工厂类只能创建一个具体产品类的实例

优点:

  1. 符合开-闭原则:新增一种产品时,只需要增加相应的具体产品类和相应的工厂子类即可
  2. 符合单一职责原则:每个具体工厂类只负责创建对应的产品

缺点:

  1. 增加了系统的复杂度:类的个数将成对增加
  2. 增加了系统的抽象性和理解难度
  3. 一个具体工厂只能创建一种具体产品

抽象工厂模式

image.png 定义:提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类;具体的工厂负责实现具体的产品实例。
解决的问题:每个工厂只能创建一类产品(工厂方法模式)

抽象工厂模式与工厂方法模式最大的区别:抽象工厂中每个工厂可以创建多种类的产品;而工厂方法每个工厂只能创建一类

举个例子,我们去买水果,需要购买 苹果和菠萝,苹果有河北苹果,也有山东苹果,那么我们购买的时候就可以进行选择,那有一天我们想购买河南苹果,如果更好的创建工厂模式呢?

首先创建水果接口,苹果/梨抽象类,及各地区的实现方法

interface Fruit{
    public void buy();
}

abstract class Apple implements Fruit{
    public abstract void buy();
}
abstract class Pear implements Fruit{
    public abstract void buy();
}

class HeBeiApple extends Apple{
    @Override
    public void buy() {
        System.out.println("购买河北苹果");
    }
}

class ShanDongApple extends Apple{
    @Override
    public void buy() {
        System.out.println("购买山东苹果");
    }
}

class HeBeiPear extends Pear{
    @Override
    public void buy() {
        System.out.println("购买河北梨");
    }
}
class ShanDongPear extends Pear{
    @Override
    public void buy() {
        System.out.println("购买山东梨");
    }
}

创建抽象工厂及具体工厂

//抽象工厂
interface FruitFactory{
    public Fruit getApple();
    public Fruit getPear();
}
//具体工厂-河北工厂
class HeBeiFactory implements FruitFactory{
    @Override
    public Fruit getApple() {
        return new HeBeiApple();
    }
    @Override
    public Fruit getPear() {
        return new HeBeiPear();
    }
}
//具体工厂-山东工厂
class ShanDongFactory implements FruitFactory{

    @Override
    public Fruit getApple() {
        return new ShanDongApple();
    }
    @Override
    public Fruit getPear() {
        return new ShanDongPear();
    }
}

具体实现

public static void main(String[] args) {
    //创建河北工厂
    HeBeiFactory heBeiFactory=new HeBeiFactory();
    Fruit apple=heBeiFactory.getApple();
    apple.buy();

    Fruit pear=heBeiFactory.getPear();
    pear.buy();

    //创建山东工厂
    ShanDongFactory shanDongFactory=new ShanDongFactory();
    Fruit sdApple=shanDongFactory.getApple();
    sdApple.buy();

    Fruit sdPear=shanDongFactory.getPear();
    sdPear.buy();
}

这样当我们想创建河南工厂的时候,就不需要修改抽象工厂,只需要创建河南工厂即可。

在什么情况下应当使用抽象工厂模式

1.一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的。

2.这个系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。

3.同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。

4.系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。

抽象工厂模式的优点

  • 分离接口和实现

  客户端使用抽象工厂来创建需要的对象,而客户端根本就不知道具体的实现是谁,客户端只是面向产品的接口编程而已。也就是说,客户端从具体的产品实现中解耦。

  • 使切换产品族变得容易

  因为一个具体的工厂实现代表的是一个产品族,比如上面例子的从河北到山东只需要切换一下具体工厂。

抽象工厂模式的缺点

  • 不太容易扩展新的产品

  如果需要给整个产品族添加一个新的产品,那么就需要修改抽象工厂,这样就会导致修改所有的工厂实现类。(比如每个地区,不仅生成苹果和梨,还会生成香蕉)

抽象工厂举例二

我们在开发项目的时候,代码需要连接数据库,并进行CRUD.项目后期可能要兼容多种数据库操作,因此我们就需要用到抽象工厂的方式,将目前可知的数据源进行抽象工厂方式创建,以后万一有其他数据源也不必修改代码,只需要创建自己的工厂即可,代码如下:

数据库连接及执行命令的工厂实现

//首先创建已知的产品--数据库连接+执行数据库命令
//创建数据库连接
interface IConnection{
    public void connected();
}
//执行数据库命令
interface ICommond{
    public void commond();
}

//创建mysql具体连接实现
class MysqlConnection implements IConnection{
    @Override
    public void connected() {
        System.out.println("mysql connection...");
    }
}
//创建mysql具体执行命令实现
class MysqlCommond implements  ICommond{
    @Override
    public void commond() {
        System.out.println("mysql commond...");
    }
}
//创建数据库连接及执行命令工厂
interface DBFactory{
    public void connection();
    public void commond();
}
//创建mysql具体实现工厂
class MysqlDBUtils implements DBFactory{
    @Override
    public void connection() {
        new MysqlConnection().connected();
    }
    @Override
    public void commond() {
        new MysqlCommond().commond();
    }
}

假如这时候Oracle数据源也需要支持,我们只需要添加Oracle相关实现,不需要修改之前的代码,即可实现如下:

//首先创建已知的产品--数据库连接+执行数据库命令
//创建数据库连接
interface IConnection{
    public void connected();
}
//执行数据库命令
interface ICommond{
    public void commond();
}

//创建mysql具体连接实现
class MysqlConnection implements IConnection{
    @Override
    public void connected() {
        System.out.println("mysql connection...");
    }
}
//创建oracle
class OracleConnection implements IConnection{
    @Override
    public void connected() {
        System.out.println("oracle connection...");
    }
}
//创建mysql具体执行命令实现
class MysqlCommond implements  ICommond{
    @Override
    public void commond() {
        System.out.println("mysql commond...");
    }
}
class OracleCommond implements ICommond{
    @Override
    public void commond() {
        System.out.println("oracle commond...");
    }
}
//创建数据库连接及执行命令工厂
interface DBFactory{
    public void connection();
    public void commond();
}
//创建mysql具体实现工厂
class MysqlDBUtils implements DBFactory{
    @Override
    public void connection() {
        new MysqlConnection().connected();
    }
    @Override
    public void commond() {
        new MysqlCommond().commond();
    }
}

class OracleDBUtils implements DBFactory{
    @Override
    public void connection() {
        new OracleConnection().connected();
    }

    @Override
    public void commond() {
        new OracleCommond().commond();
    }
}

执行代码

public static void main(String[] args) {
    DBFactory dbFactory=new MysqlDBUtils();
    dbFactory.connection();
    dbFactory.commond();

    DBFactory dbFactory1=new OracleDBUtils();
    dbFactory1.connection();
    dbFactory1.commond();
}