前两天把图片移动的东西给搞定了,那么就可以开始设计模式了,用的书是《heard first设计模式》,经典不解释。

    为了表达设计模式的核心思想,《heard first》用的是控制台输出的方式,我想让效果直观一些,于是用Qt来实现了下。

    图片移动的基础请看我的前两篇博客Qt实现图片移动Qt实现图片移动(2)定时器和信号槽 

 


    书中的例子是鸭子游戏公司,说是每添加一种鸭子,就要考虑鸭子的fly和quake方法,毕竟不同种的鸭子叫声和飞行方法不一样,有的可能不会飞(木头鸭子)。

    为了方便起见,描述一下书上的背景,更方便的是希望你手里能有这本书,电子书下载地址Head First 设计模式(中文完整版+附书源码),感谢这位朋友的分享。

 


 

    开始的时候,叫Joe的这个哥们给鸭子超类添加了一个方法fly,结果出问题了,看下面的类图:

     这是用继承的方式,使用继承,有几个问题:

    1、很难知道所有鸭子的飞行行为:父类的fly一般不能满足子类的需求,你说子类覆盖,新的鸭子覆盖fly方法?可以是可以,但是继承的目的很大一部分是想重用代码,dont repeat yourself,而且真的很难知道所有鸭子的飞行行为,例如父类飞行方法是A,子类1飞行方法和子类2的飞行方法都是B,如果用覆盖fly方法的话,难免不repeat yourself,造成代码重复

    2、运行时的行为不容易改变,也就是不能使用多态,这个我不知道怎么讲。

 


 

    这货后来想用接口,这很对,但是开始的下面想法不对,仅仅是声名了一个借口而已,让子类去实现,明显出现代码重复

    结果被骂了:

     开窍后:

最终的uml图,

    策略模式结构:


     ok,背景介绍完毕(终于完了),现在用Qt来实现

     首先来看两个基类,一个是cat,一个是runnable,见名知意,于前两篇博客不同的是,因为把动作这个概念给抽象出了一个类,所以与移动相关的东西就与cat没有关系了,可以明显的看到cat类少了很多用于移动的员和方法,而runnable专门负责运动,貌似是有个原则叫做“单一职责”,一个类的职责不该过多。

    首先是cat.h

View Code
 1 #ifndef CAT_H
 2 #define CAT_H
 3 
 4 #include <QtGui>
 5 #include "runnable.h"
 6 class Cat : public QWidget
 7 {
 8     Q_OBJECT
 9 public:
10     explicit Cat(QWidget *parent = 0);
11     virtual ~Cat();
12     void setRunnable(Runnable *r);
13 protected:
14     void mousePressEvent(QMouseEvent *event);
15     void paintEvent (QPaintEvent *event);
16     virtual void init()=0;
17 protected:
18     Runnable* runnable;
19     QPixmap* catImg;
20     QWidget* parentWidgetPointer;
21     int shrinkMultiple;
22 };
23 
24 #endif // CAT_H

    解释:Runnable* runnable;就是跑的接口,而QWidget* parentWidgetPointer;这个变量是我没办法加的,因为当runnable重绘的时候是选择重绘那个部件,而运动了之后貌似只有重绘窗口部件才能连续运动,这个不是很明白,求好心人解释,当时做的时候没有这个window的指针,图片重绘并没有实现连续移动的效果,调用了repaint函数没错,我在用qDebug函数的时候输出了一下重绘的指针,发现此时重绘的是一个Qwidget指针,而只有重绘QWindow的时候,图片才连续移动。茫然啊。。。

   cat.cpp

View Code
 1 #include "cat.h"
 2 #include "runright.h"
 3 #include "rundown.h"
 4 
 5 Cat::Cat(QWidget *parent) :
 6     QWidget(parent),shrinkMultiple(2)
 7 {
 8     parentWidgetPointer = parent;
 9 }
10 void Cat::setRunnable(Runnable *r)
11 {
12     if(!runnable)
13     {
14         runnable = r;
15     }
16     else
17     {
18         Runnable *old = runnable;
19         runnable = r;
20         delete old;
21     }
22 }
23 void Cat::mousePressEvent(QMouseEvent *event)
24 {
25     runnable->mousePressEvent(event);
26 }
27 void Cat::paintEvent (QPaintEvent *event)
28 {
29     QPainter painter(this);
30     painter.drawPixmap(*runnable->getRect (),*catImg);
31 }
32 
33 Cat::~Cat()
34 {
35     if(catImg)
36     {
37         delete catImg;
38     }
39     if(runnable)
40     {
41         delete runnable;
42     }
43 
44 }

    于之前博客不同的是,这次移动图片使用的runnable,因此图片的rect信息我存在runnable里面了,所以画图的时候使用了这么一句

painter.drawPixmap(*runnable->getRect(),*catImg);

 

   剩下的没什么好说的,接下来是runnable.h

View Code
 1 #ifndef RUNNABLE_H
 2 #define RUNNABLE_H
 3 
 4 #include <QtGui>
 5 
 6 class Runnable : public QWidget
 7 {
 8     Q_OBJECT
 9 public:
10     explicit Runnable(QRect *rect,int speed,int time,
11                       QWidget *window,QWidget *parent=0);
12     virtual ~Runnable();
13     void mousePressEvent(QMouseEvent *event);
14     QRect* getRect();
15 
16 protected:
17     QTimer *timer;
18     QRect *rect;
19     QWidget *window;
20     int speed;
21     int time;
22     bool stop;
23 signals:
24     void runSignal(QRect *rect);
25     void clicked();
26 public slots:
27     void run();
28     void timerTurnSlot();
29     virtual void runSlot(QRect *rect)=0;
30 
31 };
32 
33 #endif // RUNNABLE_H

    有一个纯虚函数的槽的槽,是让子类实现的,将runnable设置为接口。

virtual void runSlot(QRect*rect)=0;

    看runnable.cpp的实现

View Code
 1 #include "runnable.h"
 2 
 3 Runnable::Runnable(QRect *rect,int speed,int time,
 4                    QWidget *window, QWidget *parent) :
 5     QWidget(parent)
 6 {
 7 
 8     stop = true;
 9     this->window = window;
10     this->speed = speed;
11     this->time = time;
12     timer = new QTimer(this);
13     this->rect = new QRect(rect->topLeft (),rect->bottomRight ());
14     connect( this, SIGNAL( clicked() ), this, SLOT( timerTurnSlot() ) );
15     connect( this, SIGNAL( runSignal(QRect *) ), this, SLOT( runSlot(QRect *)) );
16     connect( timer,SIGNAL(timeout()),this,SLOT(run()));
17 }
18 
19 QRect* Runnable::getRect()
20 {
21     return rect;
22 }
23 void Runnable::mousePressEvent(QMouseEvent *event)
24 {
25 
26     if (rect->contains (event->x(),event->y()))
27     {
28         stop = !stop;
29         emit clicked();
30     }
31 }
32 void Runnable::run()
33 {
34     emit runSignal(rect);
35 }
36 void Runnable::timerTurnSlot ()
37 {
38     if(!stop)
39     {
40         timer->start (time);
41     }
42     else
43     {
44         timer->stop ();
45     }
46 
47 }
48 Runnable::~Runnable()
49 {
50     delete timer;
51     delete rect;
52 }

    于之前不同的是,这次我的信号槽里面有参数,当然可以没有,使用之前的方式就是,不过由于对Qt接触不多,顺便学习了一下使用参数的信号槽。

    有参数的时候,信号与槽的参数类型要对应,但定时器里面的timeout()这个方法默认就没有参数,因此我加了一个run()的槽来发射runSignal,这样子有参数的信号和槽就能对应起来了。

 

    接下来看子类的实现,rundown,意思是图片向下跑,直接把俩文件贴上:

    rundown.h

View Code
 1 #ifndef RUNDOWN_H
 2 #define RUNDOWN_H
 3 #include <QtGui>
 4 #include "runnable.h"
 5 class RunDown : public Runnable
 6 {
 7 public:
 8     explicit RunDown(QRect *rect,int speed,int time,
 9                      QWidget *window,QWidget *parent=0);
10     ~RunDown();
11 public slots:
12     void runSlot(QRect *rect);
13 };
14 
15 #endif // RUNDOWN_H

    rundown.cpp

View Code
 1 #include "rundown.h"
 2 
 3 
 4 RunDown::RunDown(QRect *rect,int speed,int time,
 5                      QWidget *window,QWidget *parent):
 6       Runnable(rect,speed,time,window,parent)
 7 {
 8 }
 9 
10 void RunDown::runSlot(QRect *rect)
11 {
12 
13     int height = rect->height ();
14     this->rect->setY (rect->y () + speed);
15     this->rect->setHeight (height);
16     window->repaint ();
17 }
18 RunDown::~RunDown()
19 {
20 }

    调用父类构造函数,重写runSlot这个槽即可。

    cat子类DownCat意思是向下跑的猫

View Code
 1 #ifndef DOWNCAT_H
 2 #define DOWNCAT_H
 3 #include "cat.h"
 4 class DownCat : public Cat
 5 {
 6 public:
 7 
 8     DownCat(QWidget *parent);
 9     ~DownCat();
10 protected:
11     void init();
12 };
13 
14 #endif // DOWNCAT_H
View Code
 1 #include "downcat.h"
 2 #include "rundown.h"
 3 DownCat::DownCat(QWidget *parent):
 4     Cat(parent)
 5 {
 6     init ();
 7 }
 8 
 9 void DownCat::init()
10 {
11     catImg = new QPixmap(":/img/black_cat.jpg");
12     int width = catImg->width ()/shrinkMultiple;
13     int height = catImg->height ()/shrinkMultiple;
14     runnable = new RunDown(new QRect(10,10,width,height),
15                             5,50,parentWidgetPointer,this);
16 }
17 DownCat::~DownCat()
18 {
19 
20 }

    多态的实现就是init里面的这句,也正是因为多态,图片运动方式可以很容易的改变,比如加一个mousePressEvent,右键点击的时候将runnable变为RunRight,额,这个时候别忘了delete之前的东西,否则会泄露的

runnable = new RunDown(new QRect(10,10,width,height),
                            5,50,parentWidgetPointer,this);

    代码分享到了我的百度网盘:策略模式Qt实现图片移动版

    贴一贴图片吧,动的效果静态图片真心看不住来,有什么办法么、、

开始的时候

    点击俩猫后,上面的猫右移,下面的猫左移:

 

posted on 2013-01-28 13:43  duoduo3_69  阅读(2145)  评论(0编辑  收藏  举报