设计模式学习笔记



  • 设计模式学习笔记

    1. 设计模式简介

    1. 设计模式是什么

    ​ 设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。简单来说,设计模式就是被人总结出来的一些写好代码的方法或者套路。

    2. 设计模式为了什么

    ​ 使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。毕竟是套路得人心嘛。

    3. 设计模式的分类

    1. 创建型模式
    2. 结构型模式
    3. 行为型模式

    2. 设计模式中的六大原则

    1. 单一职责原则

    ​ 一个类只负责一件事,一个类应该仅有一个变化的原因。如果有多个动机来改变一个类,说明这个类所承担的职责过多。

    2. 开放封闭原则

    ​ 对扩展开放,对更改封闭。个人认为是面向对象最重要的一个原则,添加代码相比于修改已有的代码更为安全,也更能避免程序成为屎山。

    3. 依赖倒置原则

    细节依赖于抽象,而不是抽象依赖于细节,简单来说,不要面对实现编程,要面对接口编程,要装电脑,而不是装收音机。

    4. 迪米特原则

    尽量减少一个对象对另一个对象的了解,这样能保证在一个类修改时,减少对于其他类的影响。保持类和类的松耦合关系。

    5. 里氏替换原则

    能够使用父类的地方应该可以安全的替换成子类。该原则在我目前看来是开放封闭原则的一种实现方式,该原则要求我们尽量将父类设置成抽象类或者接口,用子类来重写所需要的方法,在运行时再确定需要的子类类型,用子类实例来替换父类实例。(kenzo代码中比如 IEventData 类就是相似这种方式的实现)

    6. 接口隔离原则

    建立单一接口而不是建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。也就是说,我们要为各个类建立专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。

    3. UML类图

    UML图一般用于表示程序中类之间的关系,下面给出UML图的一个样例:

    image-20200622124219598

    该图片截图自《大话设计模式》

    类的定义和表示

    image-20200622124518513

    该矩形框表示一个类(Class),分为三层

    1. 第一层为类名,抽象类可用 斜体 表示
    2. 第二层为该类的一些属性和一些字段
    3. 第三层为该类的方法

    其中,public用 “+” 表示,private用 “-” 表示,protected 用 “#” 表示。

    接口的定义和表示

    image-20200622125143704

    接口和类的定义类似,仅仅在类名的上面增加了一个interface标识。

    继承关系

    image-20200622125333495

    继承关系用实线加三角形来表示,三角形指向的是父类,父类中实现的方法和属性在子类中不用重新表示。

    接口实现关系

    image-20200622125518529

    接口实现是用虚线加三角形来表示的,三角形指向的是被实现的接口。接口中的方法由于一般需要重写,所以需要在子类中表示。

    关联关系

    ​ 当一个类需要知道另一个类时,可以用关联方式来表示这种关系。

    image-20200622130004534

    关联关系时用一个带箭头的实现来表示的,箭头指向的为被关联的类。

    聚合关系

    聚合关系表示的是一种弱拥有关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分。

    image-20200622130303978

    聚合关系是由一个空心菱形和一条带箭头的实线来表示的。箭头指向的为被聚合的类。

    组合(合成)关系

    合成(组合)关系是一种强拥有关系,体现了严格的部分和整体的关系,部分和整体的生命周期相同。

    image-20200622130741390

    组合关系是由一个实心菱形和一条带箭头的实线来表示的。箭头指向的为被组合的类。

    同时在连线边的1,2为基数,表示这端的类可以有几个实例,如果一个类可以有无数个实例,则可以用n表示。关联关系,聚合关系也可以有基数。

    依赖关系

    依赖关系体现一个类需要有另一些类的存在。

    image-20200622131101478

    依赖关系用虚线表示,箭头指向被依赖的类。

    设计模式的三大分类

    创建型模式

    单例模式、建造者模式、工厂模式、原型模式。

    结构型模式

    适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。

    行为型模式

    模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。

    具体代码分析

    工厂模式

    工厂模式的本质应当就是对获取对象过程的抽象。同时,在该过程中可以在工厂类进行一些判断,筛选等,返回对应的所希望的类的实例化。

    工厂模式可以分为三种:简单工厂模式,抽象工厂模式,工厂方法模式。

    主要有以下三种类或者对象:

    1. 工厂类
    2. 抽象产品类
    3. 具体产品类

    三种不同的工厂模式的区别主要在于工厂类的不同。

    简单工厂就一个工厂类,工厂方法模式中,会有一个抽象工厂,各种子类继承它,实现子类的工厂,相当于把工厂职责细分。抽象工厂模式有点相当于简单工厂模式和工厂方法模式的结合。

    简单工厂模式

    下面就是一个简单工厂模式的使用实例:

    //filepath:   \...\Include\Framework\ComponentRepository.h
    
    //部分节选
    //组件工厂,单例类:从DLL中加载组件
    class FWK_EXP ComponentRepository: public Singleton
    {
    public:
    	DECLARE_CU_SINGLETON(ComponentRepository)
    protected:
    	ComponentRepository(void);
    	~ComponentRepository(void);
    public:
    	void Load();															//根据缓存注册数据加载组件
    	void Unload();															//卸载所有组件
    	void Register();														//注册组件
    	void Unregister();														//启动组件运行
    	void Start();
    	void Stop();
    	void LoadComponent(CString fileName,CString domain,DocFormat fmt);		//根据指定配置文件 加载组件
    	void RegComponent(CString modName,CString className,CString comName);	//组件组件
    	void RegComponent(CString modName,CString comName);
    	IComponent* GetComponent(CString name);									//获取指定组件接口
    	void GetComponents(std::vector<IComponent*>& buf);						//获取所有组件
    private:
    	// 字符串辅助函数
    	CString GenDLLName(CString modName);									//获取组件 DLL名称,创建函数名称,组件实现类名称
    	CString GenCreateFuncName(CString className);
    	CString GenClassName(CString comName);
    	
    	// 读取并加载组件名
    	bool ValidateComName(CString name);											//名称唯一性验证
    	void LoadFromINIFile(CString fileName,CString domain);						//INI配置文件加载组件实现	
    	void LoadFromXMLFile(CString fileName,CString domain);						//XML配置文件加载组件,赞未实现	
    	void RegFullComponent(CString modName,CString className,CString comName);	//注册组件全名实现
    private: 
    	std::vector<ModuleData> m_regBuf;	//组件注册数据缓存
    	std::vector<IComponent*> m_ComBuf;	//组件缓存
    	std::vector<HMODULE> m_modrefBuf;	//DLL模块句柄缓存
    };
    

    大致可以知道该工厂模式根据传入的字符串,来返回不同组件的指针。

    工厂方法模式

    image-20200622222059424

    image-20200622222118750

    情景:两种人:当代大学生和社区志愿者,均要学习雷锋,帮助老人。(情景来源于 《大话设计模式》)

    UML图如下:

    image-20200629150354847

    代码如下:

    //Factory.h
    /**
     * \brief 工厂抽象类
     */
    class IFactory
    {
    public:
    	virtual ~IFactory() = default;
    	virtual LeiFeng* createLeiFeng() = 0;
    };
    
    class VolunteerFactory final : public IFactory
    {
    public:
    	LeiFeng* createLeiFeng() override;
    };
    
    class StudentFactory final : public IFactory
    {
    public:
    	LeiFeng* createLeiFeng() override;
    };
    
    
    //Factory.cpp
    LeiFeng* VolunteerFactory::createLeiFeng()
    {
    	LeiFeng* p_v = new Volunteer();
    	return p_v;
    }
    
    LeiFeng* StudentFactory::createLeiFeng()
    {
    	LeiFeng* p_s = new Student();
    	return p_s;
    }
    
    //LeiFeng.h
    class LeiFeng
    {
    public:
    	virtual ~LeiFeng() = default;
    	virtual void helpDo1() = 0;
    	virtual void helpDo2() = 0;
    };
    
    class Student final : public LeiFeng
    {
    public:
    	virtual void helpDo1() override;
    	virtual void helpDo2() override;
    };
    
    class Volunteer final : public LeiFeng
    {
    public:
    	virtual void helpDo1() override;
    	virtual void helpDo2() override;
    };
    
    //LeiFeng.cpp
    void Student::helpDo1()
    {
    	std::cout << "student help do thing 1  " << std::endl;
    }
    
    void Student::helpDo2()
    {
    	std::cout << "student help do thing 2  " << std::endl;
    }
    
    void Volunteer::helpDo1()
    {
    	std::cout << "volunteer help do thing 1  " << std::endl;
    }
    
    void Volunteer::helpDo2()
    {
    	std::cout << "volunteer help do thing 2  " << std::endl;
    }
    
    //main.cpp
    int main()
    {
    	IFactory* factory1 = new StudentFactory();
    
    	LeiFeng* student1 = factory1->createLeiFeng();
    
    	LeiFeng* student2 = factory1->createLeiFeng();
    
    	IFactory* factory2 = new VolunteerFactory();
    
    	LeiFeng* volunteer1 = factory2->createLeiFeng();
    
    	LeiFeng* volunteer2 = factory2->createLeiFeng();
    
    
    	student1->helpDo1();
    	student2->helpDo2();
    	volunteer1->helpDo1();
    	volunteer2->helpDo2();
    	
    	return EXIT_SUCCESS;
    }
    
    工厂方法模式和简单工厂模式的一些比较

    ​ 工厂方法模式是简单工厂模式的进一步抽象。

    ​ 在简单工厂中,可以把生成哪一种类的对象的逻辑放在工厂中,但在工厂方法模式中,由于此时是由子工厂类决定返回的类型,实例化哪个类型的子类成为了客户端的需求。

    工厂方法模式克服了简单工厂模式违背开放—封闭原则的缺点,避免了修改工厂类的代码。

    ​ 但工厂方法模式并没有解决掉 if-else 或者 switch 的相关逻辑代码,只是将简单工厂中这类逻辑代码转移到了客户端进行实现。

    装饰模式

    装饰模式介绍

    image-20200622190729489

    image-20200622202014126

    情景:对一个Person类进行装饰(情景来源于《大话设计模式》)

    也可以将Person类作为抽象类,继承出子类比如Man、Woman等等,在此就未进行该方面的继承,直接使用Person类进行初始化和进行装饰。

    image-20200624182116218

    C++代码如下:

    //Person类的声明
    class Person
    {
    public:
    	Person();
    	Person(string strName);
    	virtual void showDecorate();
    private:
    	string m_Name;
    };
    
    //Person类中方法的实现
    Person::Person()
    {
    }
    
    Person::Person(string strName)
    {
    	m_Name = strName;
    }
    
    void Person::showDecorate()
    {
    	cout << m_Name << "的装饰:" << endl;
    }
    
    
    //Decorator抽象类的声明(此抽象类是意义上的抽象,不是实际语法上的抽象类)
    class Decorator :
        public Person
    {
    public:
        Decorator(Person* person);
    
        virtual void showDecorate();
    
    private:
        Person* mp_person;
    };
    
    //类中一些方法的实现
    Decorator::Decorator(Person* person):mp_person(person)
    {
    }
    
    void Decorator::showDecorate()
    {
    	mp_person->showDecorate();
    }
    
    //DecoratorA类声明
    class DecoratorA :
        public Decorator
    {
    public:
        DecoratorA(Person* person);
    
        void showDecorate();
    
    private:
        void AddDecorate();
    };
    
    //DecoratorA类方法的实现
    DecoratorA::DecoratorA(Person* person):Decorator(person)
    {
    }
    
    void DecoratorA::showDecorate()
    {
    	Decorator::showDecorate();
    	AddDecorate();
    }
    
    void DecoratorA::AddDecorate()
    {
    	cout << "A	";
    }
    
    //DecoratorB类声明
    class DecoratorB :
        public Decorator
    {
    public:
        DecoratorB(Person* person);
    
        void showDecorate();
    
    private:
        void AddDecorate();
    };
    
    //DecoratorB类方法的实现
    DecoratorB::DecoratorB(Person* person):Decorator(person)
    {
    }
    
    void DecoratorB::showDecorate()
    {
    	Decorator::showDecorate();
    	AddDecorate();
    }
    
    void DecoratorB::AddDecorate()
    {
    	cout << "B	";
    }
    
    //main函数
    
    int main()
    {
    	string personName = "qwer";
    
    	Person* person = new Person(personName);
    
    	Person* pda = new DecoratorA(person);
    
    	Person* pdb = new DecoratorB(pda);
    
    	pdb->showDecorate();
    	
    	return 0;
    }
    
    装饰模式总结

    该模式利用了C++中虚函数的性质,子类重写虚函数,修改虚函数表指针。

    装饰模式是为已有功能动态地添加更多功能的一种方式。这种模式可以将类中的一些相关的装饰功能移除,利用子类的方式动态添加,使最初的类更简单,同时还可以除去最初类中一些重复的装饰逻辑。

    装饰模式提供了更加灵活的向对象添加职责的方式。可以用添加和分离的方法,用装饰在运行时刻增加和删除职责。装饰模式提供了一种“即用即付”的方法来添加职责。它并不试图在一个复杂的可定制的类中支持所有可预见的特征,相反,你可以**定义一个简单的类并给出装饰的方法接口,此后就可以用装饰类不断重写该方法接口给它逐渐地添加功能。**便可以从简单的部件组合出复杂的功能。

    装饰模式重点在装饰,对核心功能的装饰作用;将继承中对子类的扩展转化为功能类的组合,从而将需要对子类的扩展转嫁给用户去进行调用组合,用户如何组合由用户去决定。

    观察者模式

    观察者模式介绍

    image-20200801202258510

    image-20200801202327167

    此处就不贴《大话设计模式》里的例子了。

    //filepath:\...\Include\Framework\EventArea.h
    //事件域基类型,定义事件分发,处理相关机制
    class FWK_EXP EventArea:public IEventArea
    {
    public:
    	EventArea(void);
    	~EventArea(void);
    	//注册,解除 事件处理器
    	void RegHandler(const ID& id,IEventHandler* pHandler);
    	void UnregHandler(const ID& id,IEventHandler* pHandler);
    protected:
    	//事件分发
    	void FindHandlers(const ID& id,std::vector<IEventHandler*>& buf);			// 返回指定ID的所有handler
    	void DispatchEvent(const ID& id,IEventData* pData,bool bwholeLock=true);	// 事件调度
    	//清理
    	void Clear();
    protected:
    	AutoCreateLock<CriticalSectionLock> m_HandleLock;		// 锁
    	std::map<ID,std::vector<IEventHandler*>* > m_regBuf;	// handler与ID的对应关系
    };
    
    

    上述可以看作是抽象观察者(尽管不是抽象类),各种窗口类继承该类型,都可以调用RegHandler() 和 UnregHandler() 函数。

    在程序中,它观察的对象是对应的Model类。



  • 最后一段的代码是项目里相关代码,项目中构建以Model层为center的事件域,view层通过注册来观察model层,从而对事件进行响应。


登录后回复
 

Copyright © 2018 bbs.dian.org.cn All rights reserved.

与 Dian 的连接断开,我们正在尝试重连,请耐心等待