公司接到一个项目,实现1个加法器,在项目开发的不同阶段,用户又会提出各种新的要求(即变化点)
版本1:实现一个加法器,加法器中已经保存了被加数,使用加法器的时候,要给他传递一个加数,然后加法器计算并返回相加后的结果;
版本2:(1)被加数在使用时需要乘上权重值,再和加数进行运算;(2)以前的加法器还要保留,因为有部分老用户还会使用老的代码
版本3: 老加法器必须规定被加数是非负的整数,而新加法器被加数可以是正数也可以是负数
一、结构化程序设计
(1)实现版本1
struct SAugend { int iAugend; }; int Add(struct SAugend * pSAugend,int iAddend) { return pSAugend->iAugend + iAddened; }
(2)实现版本2
struct SWeightingAugend { int iAugend; int iWeight; }; int WeightingAdd(struct SWeightingAugend *pSWeightingAugend, int iAddend) { return pSWeightingAugend->iWeight * pSWeightingAugend->iAugend + iAddend; }
用户接口,需要在用户的代码中修改函数的调用方式:
void func(...) { ... // 修改之前的函数调用 //Add(); WeightingAdd(); ... }结构化程序设计的缺点分析:
(1)、在实际应用中,通常我们会把struct结构体放到.h文件中,而方法放到其他文件中; 这样本来Add函数就是处理SAugend结构体的(他们俩本是密不可分的),却要被分到两个文件中;这就是所谓的没有封装
(2)、在新版本中,引入变化点后,需要对老代码进行修改;这就失去了代码的封闭性; 所谓代码的封闭性就是说:当项目中扩展了新的功能时,用户代码可以不用修改,就体验到新功能带来的好处(当然,如果用户不想使用新功能,也要允许他使用之前的版本)
二、基于对象的程序设计(不考虑继承,但遵循所有东西都是对象)
(1)版本1
class CAdder { public : CAdder(int iAugend) { m_iAugend = iAugend; } int Add(int iAddend) { return m_iAugend + iAddend; } private: int m_iAugend; };(2)版本2
class CWeightingAdder { public: CWeightingAdder(int iAugend, int iWeight) { m_iAugend = iAugend; m_iWeight = iWeight; } int Add(int iAddend) { return m_iAugend * m_iWeight + iAddend; } private : int m_iAugend; int m_iWeight; };用户接口:
int func(...) { ... // 使用新方法时,要修改之前的类定义和方法的调用 // CAdder ca(3); //int result = ca.Add(2); CWeightingAdder cwa(3,10); int result cwa.Add(2); }分析:
(1)、基于对象的设计,得到的改进是:变量和相关方法实现了封装
(2)、但可以看出,当变化点出现时,用户的接口还是需要修改,即还是没有封装变化点;
三、面向对象的程序设计(面向对象的设计与基于对象的设计的区别是:面向对象多了继承和虚函数)
(1)、版本1
class CAdder { public : CAdder(int iAugend) { m_iAugend = iAugend; } virtual ~CAdder() { } virtual int Add(int iAddend) { return m_iAugend + iAddend; } protected: int m_iAugend; };(2)版本2
class CWeightingAdder : public CAdder { public : CWeightingAdder(int iAugend,int iWeight):CAdder(iAugend) { m_iWeight = iWeight; } virtual ~CWeightingAdder() { } virtual int Add(int iAddend) { return m_iAugend * m_iWeight + iAddend; } protected: int m_iWeight; };
用户接口:
int func(CAdder * pAdder)
{
...
//不需要修改,同一种调用方式(父类指针指向子类对象)
int result = pAdder->Add(5);
}
分析:
面向对象的程序设计,已经能很好的封装变化点1了,即新功能的引用不影响用户的的程序接口;
关于变化点2:
先在变化点2来了,新问题是,父类的成员变量要改变,而子类的变化点不能改变,而我们使用的是子类直接继承父类成员变量的方法,父类的改变必将牵连子类;所以对于变化点2 ,面向对象的程序设计是不能实现的;
所以从这我们就知道了:继承带来的问题是,父类和子类有强藕合关系,他们失去了独立性
四、基于接口的程序设计(子类和父类之间又继承关系,变成兄弟关系;他们共同继承自他们的抽象或者说他们的共同点---都是加法器)
class IAdder { public: IAdder() { } virtual ~IAdder() { } virtual int Add(int iAddend) = 0; }
(1)、版本1
class CAdder : public IAdder { public : CAdder(unsigned int iAugend) { m_iAugend = iAugend; } virtual ~CAdder() { } virtual int Add(int iAddend) { return m_iAugend+iAddend; } private: unsigned int m_iAugend; };
(2)版本2
class CWeightingAdder : public IAdder { public : CWeightingAdder (int iAugend,int iWeight) { m_iWeight = iWeight; m_iAugend = iAugend; } virtual ~CWeightingAdder() { } virtual int Add(int iAddend) { return m_iAugend * m_iWeight + iAddend; } private: int m_iAugend; int m_iWeight; }
int func(IAdder * pAdder) { ... int result = pAdder->Adder(5); ... }