栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

软件开发必会技能:简单工厂模式、工厂方法模式、抽象工厂模式,这一篇就够了

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

软件开发必会技能:简单工厂模式、工厂方法模式、抽象工厂模式,这一篇就够了

工厂模式一般分为三种:简单工厂模式、工厂方法模式、抽象工厂模式。
在实际开发中工厂模式应该是仅次于单例模式,运用最多的一种设计模式,所以我觉得工厂模式应该是人人必会技能。

一、简单工厂模式

简单工厂模式,工厂类是创建产品的,它决定创建哪一种产品。

举个例子,在上下位机通信应用程序中,一般都会要求软件支持多种连接方式,网口、串口等。这时我们去设计软件的话第一想到的是简单工厂模式,由工厂决定创建哪种连接方式,且同一时间只能创建一种连接模式。

#include 
#include 

// 通信设备接口类
class Device
{
public:
    virtual void write() = 0;
    virtual void read() = 0;
};

///
/// 具体的子类,网口通信类
///
class EthernetDevice : public Device
{
public:
    EthernetDevice()
    {
        qDebug()<<"EthernetDevice construct";
    }

    virtual ~EthernetDevice()
    {
        qDebug()<<"EthernetDevice disconstruct";
    }

    virtual void write()
    {
        qDebug()<<"EthernetDevice write";
    }

    virtual void read()
    {
        qDebug()<<"EthernetDevice read";
    }
};

///
/// 具体的子类,串口通信类
///
class UartDevice: public Device
{
public:
    UartDevice()
    {
        qDebug()<<"uartDevice construct";
    }

    virtual ~UartDevice()
    {
        qDebug()<<"uartDevice disconstruct";
    }

    virtual void write()
    {
        qDebug()<<"uartDevice write";
    }

    virtual void read()
    {
        qDebug()<<"uartDevice read";
    }
};

class DeviceFactory //设备工厂
{
public:
    Device* createDevice(int n)
    {
        switch(n)
        {
        case 1://网口
            return (new EthernetDevice());
            break;
        case 2://串口
            return (new UartDevice());
            break;
        default:
            return nullptr;
            break;
        }
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    DeviceFactory factory;
    Device* dev = factory.createDevice(1);
    //Device* dev = factory.createDevice(2);//如果需要更换连接,只要create另外的类型即可,其他代码是不需要更改的

    dev->write();
    dev->read();
    //。。。


    return a.exec();
}

输出结果如下:

改成createDevice(2)后输出如下:

简单工厂模式主要运用c++中基类指针指向子类对象,利用c++虚函数实现多态性,实现了“一个接口,多种方法”的目的,提高了程序的灵活性、复用性、扩展性。但是如果现在新增需求,需要增加USB通信方式,就需要修改工厂类,修改switch case那里,这就违反了开闭原则:软件实体(类、模块、函数)可以扩展,但是不可修改。于是,工厂方法模式出现了。

二、工厂方法模式

工厂方法模式:不再只由一个工厂类决定那一个产品类应当被实例化,这个决定权被交给子类去做。当有新的产品(USB连接方式)产生时,只要按照抽象产品角色、抽象工厂角色提供的方法来生成即可(新连接方式可以用一个新类继承创建产品即可),那么就可以被客户使用,而不必去修改任何已有的代 码。可以看出工厂方法的结构也是符合开闭原则。如下面UML类图:

#include 
#include 

// 通信设备接口类
class Device
{
public:
    virtual void write() = 0;
    virtual void read() = 0;
};

///
/// 具体的子类,网口通信类
///
class EthernetDevice : public Device
{
public:
    EthernetDevice()
    {
        qDebug()<<"EthernetDevice construct";
    }

    virtual ~EthernetDevice()
    {
        qDebug()<<"EthernetDevice disconstruct";
    }

    virtual void write()
    {
        qDebug()<<"EthernetDevice write";
    }

    virtual void read()
    {
        qDebug()<<"EthernetDevice read";
    }
};

///
/// 具体的子类,串口通信类
///
class UartDevice: public Device
{
public:
    UartDevice()
    {
        qDebug()<<"uartDevice construct";
    }

    virtual ~UartDevice()
    {
        qDebug()<<"uartDevice disconstruct";
    }

    virtual void write()
    {
        qDebug()<<"uartDevice write";
    }

    virtual void read()
    {
        qDebug()<<"uartDevice read";
    }
};

//设备工厂,在工厂方法模式中,工厂也抽象成接口类,新增子类的同时,也需要一起新增工厂子类
class DeviceFactory
{
public:
    virtual Device* createDevice() = 0;
};

class EthernetFactory : public DeviceFactory
{
public:
    virtual Device* createDevice()
    {
         return (new EthernetDevice());
    }
};

class UartFactory: public DeviceFactory
{
public:
    virtual Device* createDevice()
    {
         return (new UartDevice());
    }
};


int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    DeviceFactory* factory = new EthernetFactory;
    //DeviceFactory* factory = new UartFactory;//如果需要更换连接,只要create另外的类型即可,其他代码是不需要更改的

    Device* dev = factory->createDevice();


    dev->write();
    dev->read();
    //。。。


    return a.exec();
}

三、抽象工厂模式

跟上面两种工厂模式差不多,绕,绕的一比,建议不要搞。

四、总结

以上三种工厂模式,在新增产品时,都存在一定的缺陷。

  • 简单工厂模式,需要去修改工厂类,这违背了开闭法则。
  • 工厂方法模式和抽象工厂模式,都需要增加一个对应的产品的具体工厂类,这就会增大了代码的编写量。

那么有什么好的方法,在新增产品时,即不用修改工厂类,也不用新增具体的工厂类?哈哈,当然有。前面三种工厂方法算是基础技能,而下面的模板工厂能算进阶技能了。

五、进阶1-模板工厂

针对工厂方法模式封装成模板工厂类,那么这样在新增产品时,是不需要新增具体的工厂类,减少了代码的编写量。

// 通信设备接口类
class Device
{
public:
    virtual void write() = 0;
    virtual void read() = 0;
};

///
/// 具体的子类,网口通信类
///
class EthernetDevice : public Device
{
public:
    EthernetDevice()
    {
        qDebug()<<"EthernetDevice construct";
    }

    virtual ~EthernetDevice()
    {
        qDebug()<<"EthernetDevice disconstruct";
    }

    virtual void write()
    {
        qDebug()<<"EthernetDevice write";
    }

    virtual void read()
    {
        qDebug()<<"EthernetDevice read";
    }
};

///
/// 具体的子类,串口通信类
///
class UartDevice: public Device
{
public:
    UartDevice()
    {
        qDebug()<<"uartDevice construct";
    }

    virtual ~UartDevice()
    {
        qDebug()<<"uartDevice disconstruct";
    }

    virtual void write()
    {
        qDebug()<<"uartDevice write";
    }

    virtual void read()
    {
        qDebug()<<"uartDevice read";
    }
};


// 抽象模板工厂类
// 模板参数:AbstractDevice_t 设备抽象类
template 
class AbstractFactory
{
public:
    virtual AbstractDevice_t *createDevice() = 0;
    virtual ~AbstractFactory() {}
};

// 具体模板工厂类
// 模板参数:AbstractDevice_t 设备抽象类,ConcreteDevice_t 设备具体类
template 
class ConcreteFactory : public AbstractFactory
{
public:
    AbstractDevice_t *createDevice()
    {
        return new ConcreteDevice_t();
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    //ConcreteFactory factory;
    ConcreteFactory factory;
    Device* dev = factory.createDevice();

    dev->write();
    dev->read();
    //。。。


    return a.exec();
}
六 进阶2-产品注册模板类

前面的模板工厂虽然在新增产品的时候,不需要新增具体的工厂类,但是缺少一个可以统一随时随地获取指定的产品对象的类。

还有改进的空间,我们可以把产品注册的对象用std::map的方式保存,通过key-valve的方式可以轻松简单的获取对应的产品对象实例。

实现大致思路:

  • 把产品注册的功能封装成产品注册模板类。注册的产品对象保存在工厂模板类的std::map,便于产品对象的获取。
  • 把获取产品对象的功能封装成工厂模板类。为了能随时随地获取指定产品对象,则把工厂设计成单例模式。
#include 
#include 
#include 

// 通信设备接口类
class Device
{
public:
    virtual void write() = 0;
    virtual void read() = 0;
};


typedef Device* (*devInstanceGenerator)();

class DevFactory
{
public:
    static DevFactory& getInstance()
    {
        static DevFactory instance;
        return instance;
    }

    const char** getDevMenu(int& num)
    {
        num = m_generators.size();
        const char** arrayHead = new const char*[num];

        int i = 0;
        for (auto g : m_generators)
        {
            size_t bufferSize = g.first.length() + 1;
            char* generatorIdBuffer = new char[bufferSize];
            strncpy_s(generatorIdBuffer, bufferSize, g.first.c_str(), g.first.length());
            arrayHead[i++] = generatorIdBuffer;
        }

        return arrayHead;
    }

    Device* getDevice(const char* name)
    {
        auto it = m_generators.find(name);
        if (it != m_generators.end())
            return it->second();

        return nullptr;
    }

    bool registerGenerator(const char* name, const devInstanceGenerator& funCreate)
    {
        return m_generators.insert(std::make_pair(name, funCreate)).second;
    }

private:
    DevFactory(){}
    DevFactory(const DevFactory&){}
    ~DevFactory(){}

    std::unordered_map m_generators;
};

namespace DevFactoryRegistrations {

    template 
    class DevFactoryRegistration
    {
    public:
        DevFactoryRegistration(const char* id)
        {
            DevFactory::getInstance().registerGenerator(
            id,
            []() {return static_cast(new T()); }
            );
        }
    };
}

///
/// 具体的子类,网口通信类
///
class EthernetDevice : public Device
{
public:
    EthernetDevice()
    {
        qDebug()<<"EthernetDevice construct";
    }

    virtual ~EthernetDevice()
    {
        qDebug()<<"EthernetDevice disconstruct";
    }

    virtual void write()
    {
        qDebug()<<"EthernetDevice write";
    }

    virtual void read()
    {
        qDebug()<<"EthernetDevice read";
    }
};
namespace DevFactoryRegistrations {
    DevFactoryRegistration _EthernetDevice("ethernet");
}

///
/// 具体的子类,串口通信类
///
class UartDevice: public Device
{
public:
    UartDevice()
    {
        qDebug()<<"uartDevice construct";
    }

    virtual ~UartDevice()
    {
        qDebug()<<"uartDevice disconstruct";
    }

    virtual void write()
    {
        qDebug()<<"uartDevice write";
    }

    virtual void read()
    {
        qDebug()<<"uartDevice read";
    }
};
namespace DevFactoryRegistrations {
    DevFactoryRegistration _UartDevice("uart");
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);


    int num;
    const char** devItems = DevFactory::getInstance().getDevMenu(num);
    qDebug()<<"register device count:"<write();
        dev->read();
        //。。。

    }


    return a.exec();
}

七 进阶总结

将工厂方法模式改良成模板工厂,虽然可以解决产品新增时,不需要新增具体工厂类,但是缺少一个可以随时随地获取产品对象的方式,说明还有改进的空间。

将模板工厂改良成产品注册模板类+单例工厂模板类,产品注册模板类用于注册不同类型的产品,单例工厂模板类用于获取指定已注册的产品对象。这种方式,可以把工厂模式中产品的注册和获取的主要功能很好的抽象成两个类,并且使用单例模式使得工厂类可以随时随地获取已注册的产品对象。

所以产品注册模板类+单例工厂模板类的工厂模式,达到了开闭法则,并且扩展性高和封装度高。应作为实际开发中首选。
代码

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/658290.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号