第一课 设计模式简介

一. 什么是设计模式

        “每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样你就能一次又一次地使用该方案而不必做重复劳动”。

——Christopher Alexander


>> 可复用是设计模式的目标;

>> 面向对象;

二. 从面向对象谈起

>> 底层思维:向下,如何把握机器底层,从微观理解对象构造;

>> 抽象思维:向上,如何将我们周围世界抽象为程序代码(其核心是帮我们管理代码复杂度);

        仅有抽象思维而没有底层思维是不够的,缺乏底层思维会导致代码出错、性能差等不良结果。

三. 软件设计复杂的根本原因


变    化


· 客户需求的变化

· 技术平台的变化

· 开发团队的变化

· 市场环境的变化

······

1. 如何解决复杂性

>> 分解:分而治之,将大问题分解为多个小问题,将复杂问题分解为多个简单问题;

>> 抽象:忽视他的非本质细节而去处理泛化和理想化了的对象模型;

2. 伪码举例

        假设要开发一个画图的程序,需要有绘制直线和矩形两种功能。

>> 分解的方式:

        我们按分解的思想,设计了如下代码:

// Shape.h
// 伪码展示,请忽略C++的一些设计规范

class Point{
public:
    int x;
    int y;
};

class Line{
public:
    Point start;
    Point end;

    Line(const Point& start, const Point& end){
        this->start = start;
        this->end = end;
    }
};

class Rect{
public:
    Point leftUp;
    int width;
    int height;

    Rect(const Point& leftUp, int width, int height){
        this->leftUp = leftUp;
        this->width = width;
        this->height = height;
    }
};

        逻辑业务处理如下(重点看OnPaint中的业务表现形式):

// MainForm.cpp

class MainForm : public Form{
private:
    // 鼠标移动会留下点的轨迹,用p1、p2表示
    Point p1;
    Point p2;

    vector<Line> lineVector;    // 线的数据结构
    vector<Rect> rectVector;    // 矩形的数据结构

public:
    MainForm(){
        // ...
    }

protected:
    virtual void OnMouseDown(cosnt MouseEventArgs& e);
    virtual void OnMouseUp(cosnt MouseEventArgs& e);
    virtual void OnPaint(cosnt PaintEvent& e);
};

void MainForm::OnMouseDown(cosnt MouseEventArgs& e){
    
    // 记录按下时点坐标
    p1.x = e.x;
    p1.y = e.y;

    // ...
    Form::OnMouseDown(e);
}

void MainForm::OnMouseUp(cosnt MouseEventArgs& e){
    
    // 记录抬起时点坐标
    p2.x = e.x;
    p2.y = e.y;

    if(直线按钮按下){
        // 构建并储存直线数据
    }
    else if(矩形按钮按下){
        // 构建并储存矩形数据
    }

    // ... 
    Form::OnMouseUp(e);
}

void MainForm::OnPaint(cosnt PaintEvent& e){

    // 针对直线
    for(int i = 0; i < lineVector.size(); i++){
        // 直线业务逻辑
    }
    
    // 针对矩形
    for(int i = 0; i < rectVector.size(); i++){
        // 矩形业务逻辑
    }
}

>> 抽象的方式:

        下面代码与分解不同的是,在设计类的时候,增加了抽象类Shape,并且在子类中增加了Draw的实现方法:

// Shape.h

class Shape{
public:
    virtual void Draw(const Graphics& g)=0;
    // 只有写了虚析构,子类通过多态释放时,子类的析构函数才会被正确调用到
    virtual ~Shape(){}
}

class Point{
public:
    int x;
    int y;
};

class Line : public Shape{
public:
    Point start;
    Point end;

    Line(const Point& start, const Point& end){
        this->start = start;
        this->end = end;
    }

    // 实现自己的Draw,负责画自己
    virtual void Draw(const Graphics& g){
        // 绘制逻辑...
    }
};

class Rect : public Shape{
public:
    Point leftUp;
    int width;
    int height;

    Rect(const Point& leftUp, int width, int height){
        this->leftUp = leftUp;
        this->width = width;
        this->height = height;
    }

    // 实现自己的Draw,负责画自己
    virtual void Draw(const Graphics& g){
        // 绘制逻辑...
    }
};

    MainForm的属性中,不再维护具体的绘图类,而是维护了绘图类的抽象接口Shape类,在OnPaint函数中,通过多态的方式,调用子类的Draw方法:

// MainForm.cpp

class MainForm : public Form{
private:
    // 鼠标移动会留下点的轨迹,用p1、p2表示
    Point p1;
    Point p2;

    // 针对所有形状
    // 这里存放的是指针类型,因为我们需要多态性
    vector<Shape*> shapeVector;    

public:
    MainForm(){
        // ...
    }

protected:
    virtual void OnMouseDown(cosnt MouseEventArgs& e);
    virtual void OnMouseUp(cosnt MouseEventArgs& e);
    virtual void OnPaint(cosnt PaintEvent& e);
};

void MainForm::OnMouseDown(cosnt MouseEventArgs& e){
    
    // 记录按下时点坐标
    p1.x = e.x;
    p1.y = e.y;

    // ...
    Form::OnMouseDown(e);
}

void MainForm::OnMouseUp(cosnt MouseEventArgs& e){
    
    // 记录抬起时点坐标
    p2.x = e.x;
    p2.y = e.y;

    if(直线按钮按下){
        // 构建并储存直线数据
        // 这里参数不可以放栈对象,必须是堆对象
        shapeVector.push_back(new Line(p1,p2));
    }
    else if(矩形按钮按下){
        // 构建并储存矩形数据
        int width = abs(p2.x - p1.x);
        int height = abs(p2.y = p1.y);
        shapeVector.push_back(new Rect(p1, width, height));
    }

    // ... 
    Form::OnMouseUp(e);
}

void MainForm::OnPaint(cosnt PaintEvent& e){

    // 针对所有形状
    for(int i = 0; i < shapeVector.size(); i++){
        shapeVector[i]->Draw(e.Graphics);  // 多态调用,各司其职
    }

    // ...
}

        从静态的角度观察,两个方式都可以实现业务功能的需求,难以分辨那种方式更好,但从变化的角度来看,假如增加了圆形的画法,按照分解的思维,会有如下改动:

// Shape.h

// ...

// 增加
class Circle : public Shape{ // ...};
// MainForm.cpp

class MainForm : public Form{
private:
    // ...
    
    // 改动
    vector<Circle> circleVector;
    
    // ...
};

void MainForm::OnMouseDown(cosnt MouseEventArgs& e){ // ... }

void MainForm::OnMouseUp(cosnt MouseEventArgs& e){
    
    // ...

    if(...){ // ...  }
    else if(...){ // ... }
    // 改动
    else if(圆形按钮按下){ // 构建并储存圆形数据 }

    // ... 
}

void MainForm::OnPaint(cosnt PaintEvent& e){

    // 针对直线
    for(...){ // ... }
    
    // 针对矩形
    for(...){  // ... }

    // 改动
    // 针对圆形
    for(int i = 0; i < rectVector.size(); i++){ // 圆形业务逻辑 }
}

        按抽象思维,会有如下改动:

// Shape.h

// ...

// 增加
class Circle : public Shape{ // ...};
// MainForm.cpp

class MainForm : public Form{ // ... };

void MainForm::OnMouseDown(cosnt MouseEventArgs& e){ // ... }

void MainForm::OnMouseUp(cosnt MouseEventArgs& e){
    
    // ...

    if(直线按钮按下){ // ...}
    else if(矩形按钮按下){ // ...}
    // 此处还是需要做一些改动
    // 以后用工厂模式可以消除此处的改动
    else if(圆形按钮按下){ // ...}

    // ... 
}

void MainForm::OnPaint(cosnt PaintEvent& e){ // ... }

        分解的思想,会随着业务的变更,使代码体系逐渐变得庞大,慢慢将难以维护,并且在业务增加的同时,需要对已有代码进行多处修改,这可能会导致多种不可预知的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值