美文网首页
GeekBand C++ Week9 Notes

GeekBand C++ Week9 Notes

作者: 古来征战几人回 | 来源:发表于2016-07-12 02:46 被阅读0次

    C++设计模式

    为了理解松耦合设计思想,掌握面向对象设计原则

    什么是设计模式?

    是一种解决方案的核心,可以避免重复劳动

    设计模式不等于面向对象设计模式

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

    语言构造,变易转换

    内存模型

    运行时机制

    抽象思维:向上,如何将我们的现实世界抽象为程序代码,

    面向对象

    组件封装

    设计模式

    架构模式

    深入理解面向对象:

    向下:封装,继承,多态

    向上:深刻把握面向对象机制所带来的抽象意义,理解如何利用这些机制来表达现实世界,掌握什么是好的面向对象设计

    如何解决复杂性?

    分解,人们面对复杂性有一个常见的做法:分而治之,将大问题分解为多个小问题,将复杂问题分解为多个简单问题

    抽象:更高层次来讲,人们处理复杂性有一个通用的技术,由于不能掌握全部的复杂对象,我们选择忽视它的非本质性细节而去处理泛化和理想化了的模型

    class Point{

    public:

        intx;

        int y;

    };

    class Line{

    public:

        Pointstart;

        Pointend;

        Line(constPoint& start, const Point& end, ){

            This->start= start;

            This->end= end;  

        }

    };

    抽象的过程

    在shape里面有虚拟方法draw,一个形状负责画自己,实现自己的draw.

    在子类中overide自己父类的虚函数

    virtual void Draw(const Graphics& g){

    g.DrawLine(Pens,Red, start.x, start.y, end.x, end.y);

    }

    class MainForm:public Form{

    private:

    pointp1;

    point p2;

    vector

    shapeVector;//多态

    public:

    }

    在后面对shapevector中的元素进行多态调用。

    两种方法的区别,哪种更好?

    客户需求的变化:

    如果客户需要多加一个圆

    //增加一个类

    class Circle{

    };

    在mainform里增加一个

    vector CircleVector

    如果检测到要画圆则要判断将圆push——back到圆的vector里

    然后刷新以后,要把圆显示出来

    用抽象的方法,建立新的circle类

    class Circle: public shape{

    public:

    //负责自己的draw

    }

    vector不需要动,因为是shape*指针

    全都不用改变除了刷新

    工厂模式里在刷新一个圆的时候也不需要改变

    重用性得到了很高的提升

    当需求变化的时候,更加方便

    DRY!!!

    由于不能掌握全部的复杂对象,处理泛化的问题

    面向对象的设计原则

    变化是复用的天敌,面向对象设计最大的优势在于抵御变化。

    理解隔离变化

    从宏观层面来看,面向对象的构建方式更能适应软件的变化,能将变化所带来的影响减为最小。

    各司其职

    从微观层面来看,面向对象的方式更强调个各类的责任

    在第一个例子里,画线的责任从mainform到了形状自己,接口一样但实际不一样。

    对象是什么?

    从语言实现层面来看,对象封装了代码和数据,

    从规格层面来看,对象是一系列可以被使用的公共接口

    从概念层面来看,

    面向对象的设计原则//设计原则比模式更重要

    依赖倒置原则(DIP)

    高层模块(稳定)不应该依赖于低层模块(变化),二者都应该依赖于抽象(稳定)

    抽象(稳定)不应该依赖于实现细节(变化),实现细节应该依赖于抽象(稳定)。

    开放封闭原则(OCP)

    对扩展开放,对更改封闭

    类模块应该是可扩展的,但是不可以修改

    单一职责原则(SRP)

    一个类应该仅有一个引起它变化的原因

    变化的方向隐含着类的责任

    Liskov替换原则(LSP)

    子类必须能够替换它们的基类(IS-A)

    继承表达类型抽象

    接口隔离原则(ISP)

    不应该强迫客户程序依赖它们不用的方法

    接口应该小而完备

    优先使用对象组合,而不是类继承

    类继承通常为白箱复用,对象组合通常为黑箱复用。

    继承在某种程度上破坏了封装性,子类父类耦合度高

    而对象组合则只要求被组合的对象具有良好定义的接口,耦合度低。

    封装变化点

    使用封装来创建对象之间的分界层,让设计者可以在分界层的一侧进行修改,而不会对另一侧产生不良影响。

    针对接口编程,而不是针对实现编程。

    不将变量类型声明为具体的类,而是声明为某个接口,客户程序无需知道对象的具体类型,只需要知道对象所具有的接口。

    减少系统中各部分的依赖关系,从而实现“高内聚,低耦合”类型的设计方案。

    产业强盛的标志:接口标准化

    模板方法

    Template Method

    GOF-23模式分类

    设计模式的应用不应该先入为主,一上来就使用设计模式是对设计模式最大的误用,没有一步到位的设计模式。

    重构的关键技法:

    静态到动态,早绑定到晚绑定,继承到组合,编译时依赖到运行时依赖,紧耦合到松耦合

    组件协作模式:

    框架与应用程序的划分,组合协作模式通过晚期绑定,来实现框架和应用程序之间的松耦合,是二者之间协作时常用的模式。

    典型模式:

    template method

    strategy

    observer/event

    动机:在软件构件过程中,某项任务常常有稳定的整体操作结构,但各个子步骤却有很多改变的需求,或者由于固有的原因,比如框架和应用之间的关系,而无法和任务的整体机构同时实现

    class library{

    public:

        voidstep1(){ 

        //…

        }

        voidstep3(){

        //…

        }

        void step5(){

        }

    };//程序库开发人员

    class Application{

        voidstep2{

        }

        void step4{

        }

    };

    int main(){

        Librarylib();

        Applicationapp();

        Lib.step1();   

        If(app.step2()){

            Lib.step3();

        }

    ….

    }

    另外一种做法:

    库的开发人员

    除了1,3,5,也写step2和step4

    virtualbool step2(){}

    virtualvoid step4(){}

    把run()写在类里,1,3,5是protected,虚的析构函数。

    子类重写实现

    library* pLIb = new Apllication();

    plib->run();

    delete plib;

    前一种方法lib开发人员开发1,3,5三个步骤,app开发人员开发2,4两个步骤,和程序主流程

    另一种lib开发人员写1,3,5三个步骤和程序主流程,app开发人员开发2,4两个步骤。

    第一种是app调用lib的,第二种是lib的调用app的

    第一种写法是一种早绑定的写法,因为lib一般写的早,程序库写的早。晚的东西调用早的东西,但在面向对象语言中,有晚绑定的机制,lib写的早但是它调用app,所以是晚绑定。模式定义一个操作算法的骨架(稳定),而将一些步骤延迟到子类中,template method是的子类可以不改变一个算法结构即可以重定义override,重写该算法的某些特定步骤。

    为什么叫template method?run就是一个样板

    稳定中有变化,2,4支持变化,是虚函数的多态调用

    c++中稳定的要写成非虚函数,变化的要写成虚函数

    设计模式的假定条件是必须有一个稳定点

    也一定有变化,设计模式的最大的作用就是在稳定和变化之间寻找一个平衡点,把兔子关进笼子里。

    在具体实现方面,被template method调用的虚方法可以具有实现,也可以没有任何实现(抽象方法,虚方法),但一般推荐把它们设置为protected方法。

    “不要调用我,让我来调用你”的反向控制结构。

    策略模式:

    strategy策略模式是一个组件协作类的模式,实现框架和应用程序之间的松耦合

    动机:在软件构件过程中,有些对象使用的算法可能多种多样经常改变,如果将这些算法都编码到对象中,将会使对象变得复杂,有时候支持不适用的算法也是一个性能负担,透明得更改,使算法和对象解耦。

    Enum taxbase{

    CN_Tax,

    Us_tAX

    dE_TAX

    };

    class SalesOrder{

        texbasetax;

    public:

        doublecalculateTax(){

        if(tax== cn_tax){

        }

        else if(tax ==us_tax){

        }

        else if(tax==de_tax){

        }

    }

    };

    有没有可能未来支持其他国家的税法

    class taxStrategy{

    public:

    virtualdouble calculate(const context& context) = 0;

    virtual~taxstrategy(){}

    };

    class CNTax:public taxstrategy{

    public:

    virtualdouble calculate(const context& context){}

    };

    class ustax: public taxstrategy{

    public:

    virtualdouble calculate(const context& context){}

    };

    class salesorder{

    private:

    taxstrategy*strategy;

    public:

    salesorder(strategyfactory*strategyfactory){

    this->strategy= strategyfactory->newstrategy();

    }

    ~salesorder(){}

    public doublecalculatetax(){

    contextcontext();

    double val =

    strategy->calculate(context);//多态调用

    }

    };

    把一些列算法一个个封装起来并且使他们可以互相替换,是算法独立于客户程序(稳定)而变化(扩展,子类化)

    strategy使类型在运行时方便地根据需要在各个算法之间切换

    strategy模式提供了用条件判断语句以外的另一种选择,消除条件判断语句实在解耦合。

    如果Strategy对象没有实例变量,各个上下文可以共享一个strategy变量,从而节省对象开销。

    有很多ifelse代码不会被真正使用但是要存在缓存里,使得有用的代码被挤到主存里,但这个不是主要的好处。

    Observer/event观察者模式

    动机:需要为某些对象建立一种通知依赖关系-一个对象的状态发生改变,所有依赖对象(观察者对象)都得到通知,如果这样的依赖关系很紧密,不能很好地抵御变化。

    Class mainform:public forms{

        Textbox*txtfilepath;

        Texbox*txtfilenumber;

    Public:

        Voidbutton_click(){

        Stringfilepath = txtfilepath->gettext();

        Intnumber = atoi(txtfilenumber->gettext().c_str());

        Filesplittersplitter(filepath, number);

        Splitter.split();

    }

    };

    class filesplitter{

    stringm_filepath;

    intm_filenumber;

    public:

    filesplitter(conststring& filepath, int filenumber):

    {}

    void split(){

    //读取大文件

    //分批次向小文件中写入

    for(int I = 0; i

    //…

    }

    }

    };

    需求是提供一个进度条,来展示完成进度

    首先在maiform上有一个progressbar* progressbar成员

    在file_splitter里也放一个progressbar

    依赖:A依赖B,A在编译的时候只有B存在才能通过。

    编译是依赖,

    当我不需要用这个bar的时候会出问题,这个progressbar其实是一个通知。通知可以用抽象的方式来表达,而不是用一个控件

    class IProgress{

    public:

    virtualvoid DoProgress(float value) = 0;

    virtual~IProgress()

    };

    所以在filesplitter里就变成了

    IProgress* m_Iprogress//抽象通知机制

    If(m_Iprogress != nullptr){

    M_Iprogress->DoProgress(i+1)/m_filenumber;

    }

    然后mainform多重继承Iprogress,C++一般用到多重继承都是一个基类和接口

    装饰模式:

    Decorator装饰模式

    “单一职责模式”在软件组件设计中,如果责任划分不清晰,使用继承得到的结果往往会让子类急剧膨胀,同时充斥着重复代码,这时候关键要划清责任。

    典型的模式有decorator和bridge。

    Class stream{

    Public:

        Virtualchar Read(int number) = 0;

        Virtualvoid seek(int position) = 0;

        Virtualvoid write(char data) = 0;

        Virtual~Stream(){}

    };

    class filestream : public stream{

    };

    class Networkstream: public stream{

    };

    我们需要对流的主体进行曹组偶以后才能加密。

    Class CtyptoFileStream : public FileStream{

    Public:

        Virtualchar Read(int number){

        FileStream:read(number);//读文件流

    }

    };

    缓冲操作

    //额外的加密操作

    //额外的缓冲操作

    这个设计的问题

    最顶上是stream,被filestream, networkstream和memeorystream三种继承,然后每个分别有加密和缓冲的流

    这样流就有很多,但其中有重复的代码

    如何重构?

    取消继承,把父类当做一个对象放入类中

    然后再把各个父类做成多态,用基类来表示,会发现所有的类全都一样,只是运行的时候new出来的对象不一样。

    但是要保证流的方法是虚方法,所以要继承自基类stream

    桥模式:

    由于某些类型的固有实现逻辑,使得他们有多个变化的维度

    class messager(

    public:

    virtualvoid login

    virtualvoid sendmessage

    virtualvoid sendpicture

    virtualvoid playsound

    virtualvoid drawshape

    virtualvoid writetext

    virtual~message

    );

    我们还要支持PC平台和mobile平台的设计

    class PCMessagerBase: public Messager{

    public:

    //重写后面的几个方法

    };

    class MobileMessagerBase: public Messager{

    };

    我们会发现在不同的平台上要支持不同的功能

    class PCMessageLite: public PCmessagerBase{

    };

    class PCMessagerPerfect: publicPCMessagerBase{

    };

    class MobileMessagerLite: publicMobileMessagerBase{

    };

    后面的类可以用messager的多态来实现,然后发现后面的lite和perfect并没有重载前面的后面几个方法,所以要把messager拆分开成两个类。。

    和decorator方法很像

    如果子类里有重复的字段,都要往上提

    abstraction和implementor,在abstraction里有一个implementor的指针,并且两个东西分别有各自的子类,向两个不同的方向延伸。

    相关文章

      网友评论

          本文标题:GeekBand C++ Week9 Notes

          本文链接:https://www.haomeiwen.com/subject/qxijjttx.html