分派的概念

分派是一个过程,这个过程用来确定应用场景中具体使用的是哪个方法函数,比如在代码中可以有n个重名的甚至重参数的方法,那么具体使用的是哪个函数呢,这个对函数的确认过程即分派。

 

那么分派的准则是什么呢?是宗量,一个方法所属的对象叫做方法的接收者,方法的接收者与方法的参量统称做方法的宗量。在此基础上,分派分为单分派和多分派两种。

 

单分派根据一个宗量类型对方法进行选择,多分派(双分派)根据多个(两个)宗量对方法进行选择。

 

而目前的高级语言大都是单分派语言(c++/java/c#),具体分派方式根据阶段又分为两种:

一种是静态分派,即方法的分派发生在代码的编译阶段,对应的实现技术为函数重载,这时编译器只根据重名方法的不同参数来判断具体使用哪个方法。也就是我们所说的编译时多态。

一种是动态分派,即方法的分派发生在代码的执行阶段,对应的实现技术为函数重写,这时程序里只根据函数具体接收者来判断具体使用哪个类的方法。也就是我们所说的运行时多态。动态分派是根据运行时具体实际参数的类型来判断调用哪个函数的。这里多说一嘴,其实函数的具体接收者本质也是一个参数,参数的名称为this。

 

也就是说我们平时使用面向对象的继承、派生、多态特性即可满足我们大部分的工作需要,但是双分派的作用也是相当大的,你给函数参数传入的是基类指针(c++),双分派能根据实际指向的派生类的类型来选择具体使用哪个函数,然而c++、java等高级语言都没有实现,我们来看看静态分派,动态分派,双分派(c++虽然没有实现,但是我们也把期望的代码写了出来)的具体应用:

 

//Problem.h
class CProblem
{
public:
	CProblem(void){}
	virtual ~CProblem(void){}

	void comm_func();
	void comm_func(int a);
};

//Problem.cpp
#include 
#include "Problem.h"
void CProblem::comm_func()
{
	printf("problem common function 1 in \n");
}
void CProblem::comm_func(int a)
{
	printf("problem common function 2 in \n");
}

//ConcreteProblem.h
#include "Problem.h"
class ConcreteProblem:public CProblem
{
public:
	ConcreteProblem(void){}
	~ConcreteProblem(void){}

	void extern_func();
};

//ConcreteProblem.cpp
#include "ConcreteProblem.h"
#include 
void ConcreteProblem::extern_func()
{
	printf("extern function in\n");
}

//Processor.h
class CProblem;
class ConcreteProblem;
class CProcessor
{
public:
	CProcessor(void){}
	virtual ~CProcessor(void){}

	virtual void dealProblem(CProblem *prob);
	virtual void dealProblem(ConcreteProblem *prob);
};

//Processor.cpp
#include "Processor.h"
#include "ConcreteProblem.h"
void CProcessor::dealProblem(CProblem *prob)
{
	printf("普通人员处理普通问题\n");
	prob->comm_func();
}
void CProcessor::dealProblem(ConcreteProblem *prob)
{
	printf("普通人员处理特殊问题\n");
	prob->comm_func();
	prob->extern_func();
}

//ConcreteProcessor.h
#include "processor.h"
class ConcreteProcessor :public CProcessor
{
public:
	ConcreteProcessor(void){}
	~ConcreteProcessor(void){}

	void dealProblem(CProblem *prob);
	void dealProblem(ConcreteProblem *prob);
};

//ConcreteProcessor.cpp
#include 
#include "ConcreteProcessor.h"
#include "ConcreteProblem.h"

void ConcreteProcessor::dealProblem(CProblem *prob)
{
	printf("专职人员处理普通问题\n");
	prob->comm_func();
}
void ConcreteProcessor::dealProblem(ConcreteProblem *prob)
{
	printf("专职人员处理特殊问题\n");
	prob->comm_func();
	prob->extern_func();
}

//main.cpp
#include 
#include 

#include "ConcreteProblem.h"
#include "ConcreteProcessor.h"

int main(int argc ,char **argv)
{
	CProblem *prob = NULL;
	CProcessor *proc = NULL;
	
	//静态分派
	prob = new CProblem();
	prob->comm_func();
	prob->comm_func(10);

	//动态单分派
	proc = new CProcessor();
	proc->dealProblem(prob);

	//这里我想让专职人员处理特殊问题
//结果专职人员处理的还是普通的问题
//c++没有实现双分派
	prob = new ConcreteProblem();
	proc = new ConcreteProcessor();
	proc->dealProblem(prob);

//派生类指针指向自己的对象
//这种情况下可以实现专职人员处理特殊问题
ConcreteProblem *concProb = new ConcreteProblem();
proc->dealProblem(concProb);

	return 0;
}

上面的C++例子得到的结果:我们想要分派派生类的重名函数,只能使用动态单分派+静态单分派,双分派?不存在的。最后一种实现方式虽然能分派到派生类的重名函数,但是这明显需要应用场景知道具体派生类类型,这增加了应用场景与实际功能的耦合度,不方便扩展,如果可以实现双分派,应用场景只需要使用这些派生类统一的基类就能搞定,优势就在这。

 

虽然高级语言没有实现双分派,我们也可以变通的来实现双分派,实现方式就是动态单分派+静态单分派,我们将上面实现分派派生类重名函数的方法做一些改动:

 

ConcreteProblem *concProb = new ConcreteProblem();
proc->dealProblem(concProb);

既然使用静态单分派可以实现(重载)我们的目标,所差就是需要知道具体派生类,我们何不将上面的代码封装到ConcreteProblem 内部以接口 来实现,在调用proc->dealProblem(concProb)的时候传this指针就好了,具体如下:

//ConcreteProblem.cpp
#include "ConcreteProblem.h"
#include 
void ConcreteProblem::extern_func()
{
	printf("extern function in\n");
}

void ConcreteProblem::visit(CProcessor *proc)
{
proc->dealProblem(this);
	printf("extern function in\n");
}

这样visit仍然是具体派生类的函数,对于应用场景来说还是需要知道具体派生类,那我们将visit函数做成虚函数不就得了,完整的代码如下:

//Problem.h
#include "Processor.h"
class CProblem
{
public:
	CProblem(void){}
	virtual ~CProblem(void){}

	void comm_func();
	void comm_func(int a);

	virtual void visit(CProcessor *proc);
};

//Problem.cpp
#include 
#include "Problem.h"

void CProblem::comm_func()
{
	printf("problem common function 1 in \n");
}

void CProblem::comm_func(int a)
{
	printf("problem common function 2 in \n");
}

void CProblem::visit(CProcessor *proc)
{

	proc->dealProblem(this);
}

//ConcreteProblem.h
#include "Problem.h"
class ConcreteProblem:public CProblem
{
public:
	ConcreteProblem(void){}
	~ConcreteProblem(void){}

	void extern_func();

	void visit(CProcessor *proc);
};

//ConcreteProblem.cpp
#include "ConcreteProblem.h"
#include 

void ConcreteProblem::extern_func()
{
	printf("extern function in\n");
}

void ConcreteProblem::visit(CProcessor *proc)
{

	proc->dealProblem(this);
}

//main.cpp
#include 
#include 

#include "ConcreteProblem.h"
#include "ConcreteProcessor.h"

int main(int argc ,char **argv)
{
	CProblem *prob = NULL;
	CProcessor *proc = NULL;
	
	//静态分派
	prob = new CProblem();
	prob->comm_func();
	prob->comm_func(10);

	//动态单分派
	proc = new CProcessor();
	proc->dealProblem(prob);

	//这里我想让专职人员处理特殊问题
	prob = new ConcreteProblem();
	proc = new ConcreteProcessor();
	proc->dealProblem(prob);

	//派生类指针指向自己的对象
	//这种情况下可以实现专职人员处理特殊问题
	ConcreteProblem *concProb = new ConcreteProblem();
	proc->dealProblem(concProb);

	//自行实现的双分派,分派根据两个具体宗量来判断函数的调用
	prob = new ConcreteProblem();
	proc = new ConcreteProcessor();
	prob->visit(proc);

	return 0;
}

这种方式实现后,应用场景就不用考虑两个宗量的具体类型了,更加符合迪米特法则;

 

你可能感兴趣的:(随笔)