singleCall单来源调用解析及实现

定义:

单来源调用指一个类的生成工作只能由特定类来执行。

eg李宁牌鞋子只能由李宁专卖店生产

这个问题归结起来,也就是说在工厂模式中,指定的产品类只能通过具体的特定工厂类来生成,而不能自己new出来或者通过其他类生成。

具体的,我们就在代码实现中进行说明了。

这里我们来一步一步分析。

首先,一个类实例(对象)不能自己产生,那么。我们就需要屏蔽构造函数了。

那么,屏蔽了构造函数之后,如何获取一个实例呢。

有两种方案可以实现。

方案一:

通过继承获取构造函数执行权限。如代码

class base
{
protected:
    base()
    {
        Trace("");
    }
};
class driver:protected base
{
public:
    void test()
    {
        base b;
    }
};

int main(int argc, char const *argv[])
{
    driver d;
    d.test();
    return 0;
}

这个方案只是可以让一个类能够产生实例。单他和我们的意图严重偏离:

a实例和子类的生命周期一致。

b没有自主权。

c其他类也可以模拟他的实现,继承父类获取生成权,这显然是个垃圾方案。

方案二:类似单例模式方法处理

产品类 构造函数屏蔽,但是提供一个获取实例的共有方法

静态方法获取实例
class base
{
protected:
    base()
    {
        Trace("");
    }
public:
    static base* getInstance();
};

base* base::getInstance()
{
    return new base();
}
int main(int argc, char const *argv[])
{
    // base* p = new base();
    base * p = base::getInstance();
    return 0;
}

通过方案二,我们实现了一个类不能自己执行 base *p = new base()

类型绑定

那么下面我们的目标就是将这个产品类和具体可生产者进行绑定了。

如何绑定呢,对于一个特定的类,处理之,我们就想到了this指针

所以我们要做的就是,产品类构造函数依赖工厂类的this 指针

// 通过静态方法获取,且依赖driver的this指针。
// 但是此时也可以通过临时对象driver生成
class driver;
class base
{
protected:
    base()//driver*)
    {
        Trace("");
    }
public:
    static base* getInstance(driver*);
};

base* base::getInstance(driver*)
{
    return new base();
}

class driver
{
public:
    base* getInstance()
    {
        return base::getInstance(this);
    }
};
int main(int argc, char const *argv[])
{

    base * p = (driver()).getInstance(); //我们所期望的运行方式。
    p = base::getInstance(new driver()); //产品类抓住漏洞强行生成自我
    return 0;
}

到这里,貌似我们的目标已经达到了,但是呢。如例子中的  p = base::getInstance(new driver());这个方法,本质还是base类自主生成的。

并不能符合我们的要求。

那么到这里,我们应该如何处理呢。

我能想到的就是,不光让base建立依赖driver的this指针,同时设置权限。才能执行getinstace()

首先给出最终代码了

// 抽象接口类,提供子类行为,同时定义权限值以及权限判断给base中的getinstace方法使用
class abstractDriver
{
protected:
    bool _canCreate;
    abstractDriver(bool can)
    :_canCreate(can)
    {}
public:
    virtual bool canCreate(){
        return _canCreate;
    }
};

class base
{
protected:
    base()
    {
        Trace("");
    }
public:
    static base* getInstance(abstractDriver*);
};

// 根据依赖的this对应类是否有权限执行决定生成
base* base::getInstance(abstractDriver*dr)
{
    if (dr->canCreate())
        /* code */
        return new base();
    else
        return NULL;
}

class driver:public abstractDriver
{
public:
    driver()
    :abstractDriver(false)
    {
        Trace("");
    }
    //在获取base实例前后修改权限。保证外部权限始终伪假
    base* getInstance()
    {
        _canCreate = true;
        base* p =base::getInstance(this);
        _canCreate = false;
        return p;
    }
};

int main(int argc, char const *argv[])
{
    base* p = base::getInstance(new driver());
    cout << p <<endl;    //NULL,未生成实例
    p = (driver()).getInstance();
    cout << p <<endl;
    p = (driver()).getInstance();
    cout << p <<endl;
    return 0;
}

好了,最终的实现版本就完成了。

符合我们的目标

base类只能通过driver类生成。

最后,我们可以发现,这个实现中的getInstace方法让我们想到了单例模式的实现。

不同的是单例模式的结果是最终

a只有一个类型实例产生,

b生成方式可以是自主的 base::getinstance()

而我们的单来源调用,

a。生成实例个数不一定是一个,只是生成方式限定了

b。不能通过直接调用base::getinstance()生成。

之所以拿出来将两个模式进行比较,是因为:单来源调用并不是23种设计模式中的一种,初次看他时,我自己也是一脸蒙蔽。其次,两者都是很重要,很常用的方法。

个人源码实现github地址:https://github.com/langya0/design_pattern_study/tree/master/singleCall

23种设计模式C++实现及扩展代码:https://github.com/langya0/design_pattern_study

参考《Single Call 模式

时间: 2024-08-05 07:08:26

singleCall单来源调用解析及实现的相关文章

RHEL6-X Window System-8.图形桌面的本地调用与远程调用解析

Linux的X或X11 (X Window System)是一个基础的图形框架接口,拥有基本的图形显示.在此框架基础之上,有诸如GNOME/KDE之类的图形桌面窗口管理应用软件(Window manager).而且图形框架X11包括两大部分X Client与X Server,采用C/S主从架构.所以它是一个system而不单单只是一个组件. Linux图形桌面的本地调用与远程调用解析如图 说明: 通过图形还是文本控制台传递命令,经过内核处理后,返回相应的数据给对方 本地情况: 1.如果是图形,则

Jvm(64),方法调用----解析

继续前面关于方法调用的话题,所有方法调用中的目标方法在Class文件里面都是一个常量池中的符号引用,在类加载的解析阶段,会将其中的一部分符号引用转化为直接引用,这种解析能成立的前提是:方法在程序真正运行之前就有一个可确定的调用版本,并且这个方法的调用版本在运行期是不可改变的.换句话说,调用目标在程序代码写好.编译器进行编译时就必须确定下来.这类方法的调用称为解析(Resolution). 换句话就是说在写好代码之后通过eclipse编译之后,编译出来的结果是不会再变化了. 在Java语言中符合"

PHPCMS V9表单向导调用及分页

参考资料如下:v9_form_tlj为你的表单数据表,`flqh`,`title`,`sj`,`username`,`datetime` 为你表单内的字段,page="$_GET"为分页代码,具体调用代码如下: <div class="box"> <h5>报名情况</h5> {pc:get sql="SELECT `flqh`,`title`,`sj`,`username`,`datetime` FROM `v9_for

console.log((function f(n){return ((n &gt; 1) ? n * f(n-1) : n)})(5))调用解析

console.log((function f(n){return ((n > 1) ? n * f(n-1) : n)})(5)); 5被传入到函数,函数内部三元计算,5 > 1成立,运算结果是5*f(4),二次运算,5*4*f(3),依次类推,最终是5*4*3*2,最终返回结果是120. 如图:

第30章 混编模式(3)

30.3 事件触发器的开发(观察者模式+中介者模式) 30.3.1 场景介绍 (1)有一产品它有多个触发事件(如创建事件.修改.删除),如创建文本框时触发OnCreate事件,修改时触发onChange事件,双击时触发onDblClick事件. (2)当产品触发事件时,会创建相应的产品事件对象(ProductEvent),并将这个事件对象传递给观察者,以便通知观察者该事件的发生. (3)观察者实现上是一个事件的分发者,相当于中介模式中的中介对象,会把这个事件分发者相应的事件处理对象. 30.3.

Spring?IOC设计原理解析:本文乃学习整理参考而来

Spring IOC设计原理解析:本文乃学习整理参考而来 一. 什么是Ioc/DI? 二. Spring IOC体系结构 (1) BeanFactory (2) BeanDefinition 三. IoC容器的初始化 1. XmlBeanFactory(屌丝IOC)的整个流程 2. FileSystemXmlApplicationContext 的IOC容器流程 1.高富帅IOC解剖 2. 设置资源加载器和资源定位 3.AbstractApplicationContext的refresh函数载入

PHP面试题及答案解析(8)—PHP综合应用题

1.写出下列服务的用途和默认端口. ftp.ssh.http.telnet.https ftp:File Transfer Protocol,文件传输协议,是应用层的协议,它基于传输层,为用户服务,它们负责进行文件的传输,其默认端口是21. ssh:Secure Shell,安全外壳协议,建立在应用层和传输层基础上的安全协议.SSH是目前较可靠,专为远程登录会话和其他网络服务提供安全性的协议,其默端口是22. http:hypertext transport protocol,超文本传送协议,是

Swift中编写单例的正确方式

Swift中编写单例的正确方式 2015-12-07 10:23 编辑: yunpeng.hu 分类:Swift 来源:CocoaChina翻译活动 14 10647 Objective-CSwift单例 招聘信息: Cocos2d-x 工程师 cocos2dx手游客户端主程 wp开发 iOS开发工程师 iOS软件工程师 iOS研发工程师 iOS讲师 iOS开发工程师 iOS高级开发工程师 iOS 高级软件工程师 iOS高级开发工程师 本文由CocoaChina译者leon(社区ID)翻译自kr

IOS_地图_定位_天气预报_Block回调_单例

H:/1021/00_block回调.h /* 通过block回调 定义block代码块,目的是解析完成之后调用 返回值是 void 参数是 数组,里面的每个成员是一个NSString*/ typedef void(^WeatherFinishedBlock)(NSArray *dataList); @interface WeatherXMLPaser : NSObject // 解析器解析数据,参数1是要解析的数据,参数2是解析完毕回调的代码块 - (void)parserWeatherDat