一道面试题的解析
题目:
1.画三角形,四边形,圆形,在窗口中移动,碰到窗口边缘会自动弹回
2.通过菜单,可以增加三角形,四边形,圆形
3.对选中的图形,可以设置填充颜色,线条宽度
4.画三角形,四边形,圆形,采用bitmap和位置两种方式实现,位图实现不设置填充颜色,线条宽度,随机产生位置。
5.采用面向对象编程。
6.时间限制:1天
这道题目要求使用面向对象思想来实现,所以当我们看完题目之后,应该马上想到定义一个图形的基类,它可能像下面这个样子:
namespace draw
{
class CBaseShape
{
Public:
//构造函数
//接口函数(virtual)
virtual ~CBaseShape() = 0;
private:
//颜色属性;
//线宽属性;
};
}
然后我们分别有
draw::CRectangle 继承自==》 CBaseShape 表示==》矩形类
draw::CTriangle 继承自==》 CBaseShape 表示==》三角形类
draw::CCircle 继承自==》 CBaseShape 表示==》圆形类
……
现在我们的图形类都拥有了颜色和线宽的属性,不过光有了这些属性还不够,接下来我们还需要给它定义一些接口,才能支持我们用多态的形式来管理我们的图形类,所以除了为图形基类定义接口之外,我想我们还需要提供一个管理类。
我们怎么知道图形类需要什么样的接口呢,在这里,我们认为使用它的客户类需要怎么样使用它,我们就为它提供什么样的接口。比如:
我们需要使用图形类的绘图的功能,我们想这样使用它
std::list<CBaseShape*> m_listShapes; //里面保存各种各样的图形
。。。
list::iterator iter = m_listShapes.begin();
list::iterator end = m_listShapes.end();
for(;iter!=end;iter++)
{
//此时*iter也许是个矩形类,也许是圆形类,也许是其他类,不论它是什么类,
//我们都想通过同样的方式访问这个类(通过Draw接口)。
(*iter)->Draw();
}
再比如,我们想让我们的图形可以移动,我们想在OnTimer函数中这样用。
std::list<CBaseShape*> m_listShapes; //里面保存各种各样的图形
。。。
list::iterator iter = m_listShapes.begin();
list::iterator end = m_listShapes.end();
for(;iter!=end;iter++)
{
//此时*iter也许是个矩形类,也许是圆形类,也许是其他类,不论它是什么类,
//我们都想通过同样的方式访问这个类(通过Move接口)。
(*iter)->Move();
}
为了方便对图形类进行管理,比如图形类的创建,绘图,移动,删除,设置属性,我们可以定义一个管理类来进行管理。
class CShapeManager
{
Public:
//枚举类型enumShapeType,是图形类的标识,指定当前要创建的图形
static CBaseShape* CreateShape(enumShapeType eType);
。。。。。。
OnDraw(); //调用上面的绘图代码
OnMove(); //调用上面的移动代码
OnPointDown();
SetCurColor();
SetCurLineWidth();
。。。。。。
private:
std::list<CBaseShape*> m_listShapes;
DWORD m_dwCurColor; //当前颜色,创建图形时使用这个属性来构造图形类。
int m_nCurWidth; //当前线宽,创建图形时使用这个属性来构造图形类。
CBaseShape* m_pCurSel; //当前选中的图形
……
}
在实现这个程序的时候,我们始终都要记住一点:
让我们的图形类的客户,即CShapeManager,尽可能的只关心CBaseShape类,而不去了解它的子类。
CShapeManager ::CreateShape这个函数,有个很通俗的叫法,工厂方法。