使用模式的一个简单例子——画图程序

本文介绍使用MFC框架结合抽象工厂模式与原型模式实现一个简单的绘图程序的过程。通过定义一个抽象基类Shape,并派生出Line、Curve等具体绘图对象,实现了不同图形的绘制及复制功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用原型模式和抽象工厂模式

      部门进行mfc的基础培训,一共4课时,要求最后能够实现一个简单的画图程序。其实我从学C++开始,就一直听人在说,mfc怎么怎么落后,怎么怎么不面向对象,当时什么也不懂,就一直隔着没碰,做编程实践,也仅仅是一些简单的控制台程序,对于比较高级一些的线程编程也就是在学java时才练了一下。网络编程根本没有接触。现在想想,原来很多东西(特别是软件工程和设计模式)都是纸上谈兵,做了一个如此简单的画图程序,才突然发现原来的抽象工厂、原型模式。。。是这样用的,有了一种醍醐灌顶的感觉。

     作为风光一时的霸主级IDE——VC6.0大大简化了win32编程,单从这一点来看,他就很成功。虽然据说他面向对象部分做的很不好,但是作为M$的成熟的应用程序框架,还是有必要学习一下的,就像侯捷老师所说的那样,每一个程序员都应该掌握framework的思想。而且用VC也一样可以写出封装较好的程序,一样可以把OO做的不错。关键的不是IDE,而是我们自己的大脑,所以看csdn上的很多关于比较javaIDE哪个好的文章都感觉很无聊。

    由于讲授mfc的老师拿到的课时比较少,而且我们对mfc的基础基本为零,所以老师就以最简单的方式给我们讲mfc如何运作,如何使用mfc画一条线等等,是面向过程的方式。听明白了以后,我就想把他以面向对象的方式实现。

    基本要求很简单,就是完成画图功能,画简单的直线,画曲线,画各种形状,所以抽象出一个公共基类:shape,然后从这个类派生出一些子类,如linecurverectangle等。

 class CShape : public CObject  
{
public:
    CShape();
    
virtual ~CShape();
    
    
//原型模式
    virtual CShape* Clone() = 0;
    
    
//设置画图为纯虚函数
    virtual void DrawShape(CClientDC &dc) = 0;
    
    
//取得鼠标按下点的消息
    virtual void GetStartPoint(CPoint point) = 0;
    
    
virtual void GetEndPoint(CPoint point) = 0;
    
    
virtual void GetPoint(CClientDC &dc, CPoint point) = 0;
    
    
//设置画笔宽度
    void SetPenWidth(int width)
    
{
        m_iPenWidth 
= width;
    }

    
    
//设置画笔颜色
    void SetPenColor(COLORREF color)
    
{
        m_iPenColor 
= color;
    }

    
protected:
    
int    m_iPenWidth;
    COLORREF    m_iPenColor;    

private:
    
//禁止复制操作
    CShape(const CShape&);
    CShape
& operator=(const CShape&);    
}
;   

class CLine : public CShape  
{
public:
                      ...
protected:
    CPoint m_ptStart;
    CPoint m_ptEnd;
}
;   

class CCurve : public CShape  
{
public:
                      ...    
protected:
    CArray
<CPoint, CPoint> m_arrPointArray;
}
;                                                    

    这里小用了一把abstract factory模式将shape做成一个类似于工厂的抽象类,它其中定义了设置画笔颜色、宽度两个普通函数,然后是响应按键信息的纯虚函数GetStartPointGetEndPointGetMovePoint。子类派生的时候只需要实现这三个函数就好了。不同的子类拥有不同的数据成员,如直线拥有两个点对象,分别保存直线起始点和终止点的位置;曲线拥有一系列点的数组。

    当用户需要画直线的时候只需要想windows自带的画图程序那样,在画笔选择框上选中相应的图形即可,这时mfc相应一个何种画笔的信息,如果是直线,返回一个直线对象,如果是曲线,返回一个曲线对象,用户利用这个对象画图即可。View类中只有一个抽象类shape的指针,Doc类中保存图形中点的信息。选择操作都是由shape类型的这个指针完成。这就是一个简单的抽象工厂模式。

    当然用户不愿意每画一条线就点选一次画笔,Windows的画图程序有记忆上一次画线种类的方法,这个功能可以用另一个模式完成:原型模式,即在shape中添加一个纯虚函数clone,他的返回值是shape类型的指针,子类实现他,返回子类本身的一个对象。

CShape* CCurve::Clone()
{
    
return new CCurve;
}


CShape
* CLine::Clone()
{
    
return new CLine;
}

   

   这样在处理时,访问Doc类中保存图形信息的数组的最后一个元素,这样就可通过调用这个对象的clone操作完成,生成一个新对象的操作,而且这个对象与上一次的对象相同。

    让我比较郁闷的是,之前精心画的UML图和工厂模式图无法添加进来!优快云赶紧改一下~

### 使用MFC实现简易画图板 为了创建一个简单的基于MFC的绘图画板应用程序,可以遵循以下方法来构建此应用的核心部分。这里展示了一个简化版的例子,它允许用户通过鼠标点击和拖动的方式在窗口内绘制线条。 #### 创建项目框架 首先,利用Visual Studio中的MFC AppWizard工具建立一个新的对话框或SDI/MDI风格的应用程序模板。这一步骤会自动生成大部分基础代码结构以及必要的资源文件[^1]。 #### 实现绘画逻辑 接下来,在`CMyView`类中重载虚函数`OnLButtonDown`, `OnMouseMove` 和 `OnLButtonUp` 来捕捉用户的输入动作并响应这些事件: ```cpp void CMyView::OnLButtonDown(UINT nFlags, CPoint point) { m_bIsDrawing = TRUE; m_ptLastPos = point; CDC* pDC = GetDC(); if (pDC != NULL){ pDC->MoveTo(point); ReleaseDC(pDC); } CView::OnLButtonDown(nFlags, point); } void CMyView::OnMouseMove(UINT nFlags, CPoint point) { if (!m_bIsDrawing || !nFlags & MK_LBUTTON) return; CDC* pDC = GetDC(); if (pDC != NULL){ CPen pen(PS_SOLID, 2, RGB(0, 0, 0)); CPen *oldPen = pDC->SelectObject(&pen); pDC->LineTo(point); pDC->MoveTo(m_ptLastPos); pDC->SelectObject(oldPen); DeleteObject(pen); m_ptLastPos = point; ReleaseDC(pDC); } CView::OnMouseMove(nFlags, point); } void CMyView::OnLButtonUp(UINT nFlags, CPoint point) { m_bIsDrawing = FALSE; CView::OnLButtonUp(nFlags, point); } ``` 上述代码片段定义了当按下左键(`OnLButtonDown`)时保存起始位置;移动过程中持续绘制线段(`OnMouseMove`)直至释放按钮(`OnLButtonUp`)停止绘制过程。 此外,还可以扩展功能集,比如加入橡皮擦模式、改变笔触宽度与颜色等功能,甚至集成更复杂的图形编辑特性如形状绘制、文本标注等[^5]。 #### 添加额外功能——油漆桶效果 对于想要增加的颜色填充功能,则可以根据提供的信息使用`FloodFill` API 完成特定区域内的自动着色操作。 ```cpp case DrawType::Fill: { CClientDC dc(this); CBrush newBrush(m_BrushColor); CBrush* oldBrush = dc.SelectObject(&newBrush); dc.FloodFill(m_PointBegin.x, m_PointBegin.y, m_PenColor); dc.SelectObject(oldBrush); } break; ``` 这段代码展示了如何切换至填充分支,并调用`FloodFill`完成指定坐标的色彩替换任务。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值