从今年6月底负责一个基于面向对象设计的C++项目,功能是做卫星成像任务规划算法服务的,我主要做的是高分7号卫星,而前面已经是做了5颗卫星了。以前对面向对象设计仅局限于理论知识,知道它有封装、继承、多态的特性,也明白是如何实现以及它的好处,但只有你真正地应用到具体项目中去,才能深刻地感受到它的好处。
一、封装
将业务封装起来,提高可复用性。
二、继承
当多个不同业务继承一个基类时,减少这多个业务的耦合性。
三、多态
实现多态是有前提的:继承,方法重写和父类指针指向子类对象,好处就是易扩展、降低程序耦合性。
静态多态:比如重载,在编译期间就可以根据参数的不同来确定调用的是哪个函数或方法;动态多态:在运行期间才能确定要实例化哪个子类对象或调用哪个方法,因为用来确定调用哪个方法的这个参数有可能是在运行时输入的,比如用户输入、数据库输入、或是消息队列输入,总之是不确定的。 以负责的高分项目为例,目前共有6颗卫星,每颗卫星任务规划类都继承同一个任务规划基类,基类里定义一些必要的或者说是公共的虚函数之类的,然后在每颗星代码里重写这些方法。在规划时,通过简单工厂模式来完成对象的实例化,这个工厂的输入也就是前面提到的不确定的参数是卫星代号,根据卫星代号的不同去实例化不同的卫星任务规划子类对象,并赋值给基类指针。也就是让基类指针指向对应的子类对象,这样这个基类指针就可以调用对应的卫星的规划方法了。这样好处就是,1.卫星与卫星之间耦合性减低了,我在改动其中一颗卫星代码时,不会影响其他卫星;2.扩展性提高了,以后如果做高分8、9、10就直接再继承这个任务规划基类,然后根据卫星业务规则重写方法;3.至于复用性,我们封装了卫星与卫星之间公用的部分(如数据库操作、卫星公有业务)来提高复用性。
利用简单工厂模式进行实例化的过程如下():
void SatMetaTaskEvaluate::creatSimulateEvaluateSolver( const string &satId )
{
pConsSolver = NULL;//父类智能指针初值为NULL,根据satId(卫星代号)来指向具体的某颗卫星规划算法服务
if(satId == "GF-1" || satId == ConstraintDataItem().extendSat_Id) //GF-1
{
pConsSolver = pConstrainSolver(new SimulateAndEvaluate_gf1(m_pExtdata));
}else if(satId == "GF-2") //GF-2
{
pConsSolver = pConstrainSolver(new SimulateAndEvaluate_gf2(m_pExtdata));
}else if(satId == "GF-3")//GF-3
{
pConsSolver = pConstrainSolver(new SimulateAndEvaluate_gf3(m_pExtdata));
}
else if(satId == "GF-5")//GF-5
{
pConsSolver = pConstrainSolver(new SimulateAndEvaluate_gf5(m_pExtdata));
}
else if (satId == "GF-6")//GF-6
{
pConsSolver = pConstrainSolver(new SimulateAndEvaluate_gf6(m_pExtdata));
}
else if (satId == "GF-7")//GF-7
{
pConsSolver = pConstrainSolver(new SimulateAndEvaluate_gf7(m_pExtdata));
}
else //for GF-*
{
cerr<<"NO EXIT"+satId<<endl;
}
}
四、简单工厂模式、工厂方式模式
本项目也使用到了简单工厂模式,后来去学习了简单工厂模式和工厂模式的异同点。
简单工厂模式:用单独的工厂类来创造实例的过程。根据客户端的选择条件(参数)动态实例化相关的类。它的不好地方就是需要在工厂类内部进行逻辑判断,来实例化相关的类;如果想拓展工厂类封装的产品功能,是需要修改工厂类的。 如果工厂方法模式,它是将一个类的实例化延迟到其子类,将子类看出一个个工厂。这样想实例化具体的类,选择判断的问题还是存在的,不过与简单工厂模式不同的是,工厂方法模式的判断变成了在客户端进行判断,而不是在工厂类。