加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c – 在使用工厂模式时,我应该以任何方式避免向下转换吗?

发布时间:2020-12-16 06:56:35 所属栏目:百科 来源:网络整理
导读:我正在研究一个实现专有协议的服务器项目.服务器在C中实现工厂模式,我们现在面临向下转换的问题. 我正在研究的协议是为慢速网络的自动控制而设计的,例如RS485,ZigBee,窄带PLC等.我们设计了主服务器的工厂模式.当接收到新帧时,我们首先识别该帧的相关设备类型
我正在研究一个实现专有协议的服务器项目.服务器在C中实现工厂模式,我们现在面临向下转换的问题.

我正在研究的协议是为慢速网络的自动控制而设计的,例如RS485,ZigBee,窄带PLC等.我们设计了主服务器的工厂模式.当接收到新帧时,我们首先识别该帧的相关设备类型,调用工厂方法以生成新的“解析器”实例,并将该帧分派给解析器实例.

我们的专有协议是用纯二进制实现的,我们可能需要的每个信息都记录在帧本身中,因此基本接口可以定义得尽可能简单.我们还为我们的工厂实现了自动注册方法(这里省略了与std :: map操作相关的详细代码):

// This is our "interface" base-class
class parser
{
public:
    virtual int parse(unsigned char *) = 0;
    virtual ~parser() { }
};

// The next two classes are used for factory pattern
class instance_generator
{
public:
    virtual parser *generate() = 0;
};

class parser_factory
{
private:
    static std::map<int,instance_generator*> classDB;
public:
    static void add(int id,instance_generator &genrator);
    parser *get_instance(int id);
};

// the two template classes are implementations of "auto-regisrtation"
template <class G,int ID> class real_generator : public instance_generator
{
public:
    real_generator()    {   parser_factory::add(ID,this);   }
    parser *generate()  {   return new G;   }
};

template <class T,int N> class auto_reg : virtual public parser
{
private:
    static real_generator<T,N> instance;
public:
    auto_reg() { instance; }
};
template <class T,int N> parser_generator<T,N> auto_reg<T,N>::instance;


// And these are real parser implementations for each device type
class light_sensor : public auto_reg<light_sensor,1>
{
public:
    int parse(unsigned char *str)
    {
        /* do something here */
    }
};

class power_breaker : public auto_reg<power_breaker,2>
{
public:
    int parse(unsigned char *str)
    {
        /* do something here */
    }
};

/* other device parser */

这种工厂模式运行良好,并且很容易花费新的设备类型.

然而,最近我们试图与现有的控制系统接口,提供类似的功能.目标系统很老,它只提供基于ASCII的,类似AT命令的串行接口.我们设法解决了与PTY的通信问题,但现在要解决的问题是解析器实现.

目标系统的命令接口非常有限.我不能只是等待并听取传入的内容,我要轮询状态,我必须轮询两次 – 首先轮询头部,第二次轮询有效负载 – 以获得完整命令.这对我们的实现来说是一个问题,因为我要将两个帧传递给解析器实例,以便它可以工作:

class legacy_parser : virtual public parser
{
public:
    legacy_parser() { }
    int parse(unsigned char *str)
    {
        /* CAN NOT DO ANYTHING WITHOUT COMPLETE FRAMES */
    }
    virtual int parse(unsigned char *header,unsigned char *payload) = 0;
};

class legacy_IR_sensor : 
    public legacy_parser,public auto_reg<legacy_IR_sensor,20>
{
public:
    legacy_IR_sensor(){ }
    int parse(unsigned char *header,unsigned char *payload)
    {
        /* Now we can finally parse the complete frame */
    }
};

换句话说,我们需要调用派生类的方法,并且该方法未在基??类中定义.我们使用工厂模式生成派生类的实例.

现在我们有几个选择:

>简单地将两个字符串连接成一个不起作用.两个字符串都包含一些设备指定的信息,它们应单独解析.如果我们采用这种方法,我们将在解析字符串之前从解析器实例中执行一些“预解析”.我们认为这不是一个好主意.
>将parser_factory :: get_instance()的返回转发给legacy_parser.
>创建另一个独立工厂,该工厂仅包含派生自legacy_parser的类.
>更改instance_generator和parser_factory的定义,以便它们也可以生成(legacy_parser *),同时保持所有现有代码不受影响:

class instance_generator
{
public:
    virtual parser *generate() = 0;
    virtual legacy_parser *generate_legacy() { return NULL; }
};

class extended_parser_factory : public parser_factory
{
public:
    legacy_parser *get_legacy_instance(int id);
};

>使用访问者模式实现“智能指针”以处理从legacy_parser派生的实例:

class smart_ptr
{
public:
    virtual void set(parser *p) = 0;
    virtual void set(legacy_parser *p) = 0;
};

class parser
{
public:
    parser() { }
    virtual int parse(unsigned char *) = 0;
    virtual void copy_ptr(smart_ptr &sp)    // implement "Visitor" pattern
    {
        sp.set(this);
    }
    virtual ~parser() { }
};

class legacy_parser : virtual public parser
{
public:
    legacy_parser() { }
    void copy_ptr(smart_ptr &sp)    // implement "Visitor" pattern
    {
        sp.set(this);
    }
    int parse(unsigned char *str)
    {
        /* CAN NOT DO ANYTHING WITHOUT COMPLETE FRAMES */
    }
    virtual int parse(unsigned char *header,unsigned char *payload) = 0;
};

class legacy_ptr : public smart_ptr
{
private:
    parser *miss;
    legacy_parser *hit;
public:
    legacy_ptr& operator=(parser *rhv)
    {
        rhv->copy_ptr(*this);
        return *this;
    }
    void set(parser* ptr)
    {
        miss=ptr;
        /* ERROR! Do some log or throw exception */
    }
    void set(legacy_parser *ptr)
    {
        hit = ptr;
    }
    legacy_parser& operator*()
    {
        return *hit;
    }
    ~legacy_ptr()
    {
        if(miss) {
            delete miss;
        }
        if(hit) {
            delete hit;
        }
    }
};

很明显,使用dynamic_cast<>进行Downcasting对我们来说这是最简单的方法,但我们都不喜欢这个想法,因为我们都觉得向某些人倾斜是“邪恶的”.然而,没有人能够准确地解释为什么它是“邪恶的”.

在我们做出决定之前,我希望听到有关这些选项的更多评论.

解决方法

http://en.wikipedia.org/wiki/Circle-ellipse_problem是你邪恶的第一个例子.
如果你发现你可以做违反基本原则的事情,那么你应该发明另一个轮子或尝试另一个帽子: http://en.wikipedia.org/wiki/Six_Thinking_Hats

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读