类与类之间的关系:
**泛化关系:**用来表示类与类之间的继承关系,也叫做is a kind of关系,用三角形的箭头从子类指向父类。
**实现关系:**实现关系就是类或接口的实现,由三角箭头虚线从实现类指向接口类,(比如C++中纯虚函数的实现)。
**依赖关系:**依赖关系是指在一个类中要用到另一个类的实例的一种关系,主要表现为一个类是另一个类的函数参数,或者一个类是另一个类的函数返回值的情况。在类图中的表现形式为一个虚线箭头从一个类指向被依赖的类。
具体代码表现形式为
class Class4 {};
class Class3
{
public:
void function(Class4 temp) //Class4类对象作为Class3的成员函数的函数参数
{
/**/
}
};
**关联关系:**关联关系是类和类之间对应的一种连结,是指两个独立的对象,一个对象的实例与另一个对象的一些特定实例存在固定的对应关系。关联关系可以是单向的也可以是双向的,通过关联使得一个类可以使用另一个类的属性和方法,在C++中具体的表现形式为,一个类的对象是另一个类的成员属性。在类图中,单向的是带箭头的实线,双向的是不带箭头的实线。
代码形式为
class Class6 {};
class Class5
{
public:
void func()
{
c.func1(); //可以直接使用Class6的方法
}
private:
Class6 c; //Class6对象作为Class5类的成员属性
};
**聚合关系:**聚合关系表示整体和部分的关系,它是一种has-a的包含关系,而且整体和部分是可以分离的,即离开整体,部分也能存在。聚合关系是关联关系的一种,它是一种更强的关联关系。聚合关系在C++中的表现也是成员变量的方式,但是和关联关系不同的是,关联关系中的两个类是相互独立的,而聚合关系中的两个类是整体与部分的关系,一个类是另一个类的一部分。在类图中,空心菱形在代表整体的类那侧,另一侧为代表部分的类。
聚合关系的简单理解,比如手机和充电线,充电线是手机的一部分,手机就是整体,充电线是部分,但是没有手机了,充电线也可以单独存在。代码表现形式为:
class charger {};
class phone
{
public:
void set_charger(charger m_c)
{
this->m_c = m_c;
}
public:
charger m_c;
};
在聚合关系中,我们在创建一个对象phone的时候可以不去管charger,因为在phone类中定义了set_charger方法用于构造charger,我们可以通过该方法在其它时机设置charger。
**组合关系:**也是关联关系的一种,是一种比聚合关系更强的关联关系。如果两个类是聚合关系(A是B的一部分),那么离开B之后,A就失去了存在的意义。组合关系是一种不可分离的关系。在类图中用实心菱形指在代表整体的类,另一侧为代表部分的类。
组合关系的简单理解,屏幕screen是手机phone的一部分,并且屏幕screen离开手机phone之后就失去了存在的意义,这就是组合关系。代码表现形式为:
class screen {};
class phone
{
public:
phone(screen m_s)
{
this->m_s = m_s;
}
public:
screen m_s;
};
在组合关系中,创建phone对象的时候就已经构造了属性m_s,也就是整体phone的生命周期也决定了部分screen的生命周期,一旦phone生命周期结束了,screen的生命周期也结束了。在聚合关系中,没有这种强的生命周期的关联。
对6种类与类之间的关系进行总结对比:
- 泛化关系和实现关系的区别:泛化关系是指C++中的继承关系;而实现关系是指虚基类的继承,在子类中实现虚基类的纯虚函数。
- 泛化关系和实现关系可以看成依赖关系的一种,因为它们离开依赖的类都无法编译通过。
- 聚合关系和组合关系是关联强度逐渐增强的关联关系;关联关系双方是平等的,聚合关系和组合关系的双方是整体和部分的关系。
- 聚合关系的双方整体和部分可以分离单独存在,没有生命周期的强相关;组合关系双方,部分离开整体将失去意义,整体的生命周期代表了部分的生命周期。
当我们在使用类来new创建一个对象的时候,会自动调用构造函数,每创建一个对象都会调用构造函数来构造一个新的对象:
class classA{};
void func()
{
classA* a1 = new classA; //调用构造函数
classA* a2 = new classA; //调用构造函数
if (a1 != a2)
{
cout << "a1和a2是两个不同的对象" << endl;
}
}
//a1和a2是两个不同的对象
懒汉式单例模式:
既然我们希望这个类只有一个实例对象,那么就应该禁止类的外部访问构造函数,因为每次在类的外部调用构造函数都会构造出一个新的实例对象。解决办法就是把构造函数设置为私有属性,在类的内部完成实例化对象的创建,这样就对外隐藏了创建对象的方法。但是类的出现就是要定义对象的,我们要使用类创建的对象,所以还需要提供一个全局访问点来获取类内部创建好的对象:
- 构造函数私有化;
- 提供全局访问点;
- 内部定义一个static静态指针指向当前类定义的对象;
class SingletonPattern
{
private:
SingletonPattern()
{
cout << "私有的构造函数" << endl;
}
public: //构造函数被私有化了,所以应该提供一个对外访问的方法,来创建对象
static SingletonPattern* get_single()
{
if (single == NULL) //为保证单例,只new一次
{ //如果不加这个判断,每次创建对象都会new一个single,这就不是单例了
single = new SingletonPattern;
}
//return this->single;
return single; //静态成员属于整个类,没有this指针
}
private: //static 成员,类定义的所有对象共有static成员
static SingletonPattern* single; //指针,不能是变量,否则编译器不知道如何分配内存
};
SingletonPattern* SingletonPattern::single = NULL; //告诉编译器分配内存
int main()
{
SingletonPattern* s1 = SingletonPattern::get_single(); //在get_single中会new一个对象
SingletonPattern* s2 = SingletonPattern::get_single();
if (s1 == s2)
{
cout << "单例" << endl;
}
else
{
cout << "不是单例" << endl;
}
}
//私有的构造函数
//单例
上面程序所示的就是一个懒汉式单例模式的实现。这里面有几点要注意的:
(1)为了让这个类所定义的所有对象共享属性,应该把属性设置为static类型,因为static类型的属性属于整个类而不是属于某个对象。
(2)为了保证单例模式,应该在全局访问点get_single()函数中加一个判断,如果对象已经被创建了,那么就直接返回这个对象,如果对象还没有被创建,那么久new创建一个对象,并返回该对象。
(3)因为是在使用到对象的时候,才去创建对象(single初始化为NULL,在全局访问点get_single被调用的时候才去创建对象),有点偷懒的感觉,所以称之为懒汉式单例模式。
懒汉式单例模式的缺陷
通过懒汉式单例模式,我们实现了一个类只创建一个实例对象,且只有在用到实例对象的时候,才会通过全局访问点去new创建这个对象,节省了资源。但是,懒汉式单例模式有一个致命的缺点,就是在C++的构造函数中,不能保证线程安全。什么意思呢,也就是说,在多个线程都去创建对象,调用全局访问点get_single()的时候,会面临资源竞争问题,假如在类的构造函数中增加一个延迟函数,我们第一个线程调用get_single()的时候,会进入构造函数,这时,因为延时的存在,第一个线程可能会在这里卡顿一会,假如正好这时候第二个线程也调用get_single()去创建实例对象,而第一个线程还在构造函数中延时,这样在get_single()函数中(single == NULL)这个判断条件依然成立,第二个线程也会进入构造函数。这样,两个线程创建的对象就不再是同一个对象了,也就不是单例模式了。
懒汉式单例模式与多线程(DCL与饿汉式):
首先,我们把类改造一下,在构造函数中加一个延时,并在类中加一个计数器来记录构造函数的调用次数
class SingletonPattern
{
private:
SingletonPattern()
{
count++;
Sleep(1000); //第一个线程在new的时候,如果延时还没结束
//第二个线程又过来new一个对象,这时候因为第一个对象还没有new出来
//所以single还是NULL,这样又会进入构造函数,最后总共new了两个对象
//这样返回的两个对象是两次new出来的,就不是单例模式了
printf("私有的构造函数\n");
}
public:
static int get_count()
{
return count;
}
public: //构造函数被私有化了,所以应该提供一个对外访问的方法,来创建对象
static SingletonPattern* get_single() //只有在调用该函数的时候才会new一个对象
{
if (single == NULL)
{
single = new SingletonPattern;
}
return single;
}
private: //static 成员,类定义的所有对象共有static成员
static SingletonPattern* single;
static int count;
};
SingletonPattern* SingletonPattern::single = NULL;
int SingletonPattern::count = 0;
这样,一个类就定义好了。接下来,我们要在main进程中创建三个线程,每个线程都去创建一个对象,在Windows下多线程编程应包含头文件<process.h>,并且会用到线程创建函数beginthread(),对于beginthread()函数的使用可以直接转到源码查看函数原型
typedef void (__cdecl* _beginthread_proc_type )(void*);
typedef unsigned (__stdcall* _beginthreadex_proc_type)(void*);
_ACRTIMP uintptr_t __cdecl _beginthread(
_In_ _beginthread_proc_type _StartAddress,
_In_ unsigned _StackSize,
_In_opt_ void* _ArgList
);
该函数包含三个参数,分别代表如下含义:
第一个参数是_beginthread_proc_type,通过上面的typedef可知,它是一个回调函数(函数指针),指向新开辟的线程的起始地址;
第二个参数_StackSize是新线程的堆栈大小,可以直接给个0,表示和主线程共用堆栈;
第三个参数_ArgList是一个参数列表,它表示要传递给新开辟线程的参数,新线程没有参数的话可以传入NULL;
函数返回值可以理解为创建好的线程的句柄。
首先搭建测试程序如下:
{
int i = 0, ThreadNum = 3;
HANDLE h_thread[3];
for (i = 0; i < ThreadNum; i++)
{
h_thread[i] = (HANDLE)_beginthread(_cbThreadFunc, 0, NULL);
}
for (i = 0; i < ThreadNum; i++)
{
WaitForSingleObject(h_thread[i], INFINITE); //windows 下的等待
//thread_join //Linux 下的等待函数
//等待子线程结束,如果不等待子线程结束主进程就死掉的话,子线程也会随之死掉,所以主进程挂起等待
}
cout << "子线程已结束" << endl;
}
这里用到了一个函数WaitForSingleObject(),它用于等待子线程结束。因为子线程是依附于主线程存在的(共用堆栈、内存四区),如果子线程还没结束主线程就结束了,那么子线程也将不复存在,所以需要等待子线程结束后,主线程才能结束。该函数类似于Linux中的thread_join函数。
搭建好测试程序后,在定义一个线程函数
void _cbThreadFunc(void* arc)
{
DWORD id = GetCurrentThreadId(); //获取当前线程ID
int num = SingletonPattern::get_single()->get_count(); //创建对象
printf("\n构造函数调用次数:%d\n", num); //调用了3次构造函数 --- 不是单例
printf("当前线程是:%d\n", id);
//cout << "当前线程是:" << id << endl; //会有问题
}
饿汉式单例模式
第一种解决方法就是在类中定义static SingletonPattern*指针的时候就创建一个对象,在全局访问点get_single()直接返回创建好的对象,因为对象早就提前创建好了,这样即使多个线程调用创建对象所得到的也是同一个对象。因为对象还没使用就创建好了,所以叫做饿汉式单例模式。
上面的测试程序不用修改,我们只需要修改类即可:
class SingletonPattern
{
private:
SingletonPattern()
{
count++;
Sleep(1000);
printf("私有的构造函数\n");
}
public:
static int get_count()
{
return count;
}
public:
static SingletonPattern* get_single()
{
return single;
}
private:
static SingletonPattern* single;
static int count;
};
//SingletonPattern* SingletonPattern::single = NULL;
SingletonPattern* SingletonPattern::single = new SingletonPattern; //饿汉式单例,一开始就new了一个对象
int SingletonPattern::count = 0;
再次运行前面的测试函数,构造函数调用次数一直为1;
DCL(double-checked locking)
对临界区资源加一个锁🔒,当一个线程持有锁的时候,其他线程挂起等待锁的释放,只有持有锁的线程才能进入临界资源,这就解决了多线程资源竞争的问题(此处涉及到多线程同步问题)。这里还有一个问题,当我们第一次判断(single == NULL)后,如果之前没有创建对象,那么就进入下面的临界区
if (single == NULL)
{
cs.Lock();
single = new SingletonPattern;
cs.Unlock();
}
当第一个线程创建完对象后释放了锁,第二个线程进入临界区又创建了一个对象,这也违反了单例原则。所以应该加入一个二次检查,如果第一个线程已经创建了对象(指针不为NULL),那么第二个线程即使获取了锁,也不再创建新的对象,而是直接使用第一个线程创建的对象,这就是二次检测的原因。
static SingletonPattern* get_single()
{
if (single == NULL) //double check
{ //因为在这之前并没有保护机制,所以三个线程都有可能执行到这一步
cs.Lock();
if (single == NULL) //所以需要二次检查,进入临界区后再一次判断
{
single = new SingletonPattern;
}
cs.Unlock();
}
return single; //静态成员属于整个类,没有this指针
}
对全局访问点get_single()修改过后,再次运行测试函数,构造函数调用次数一直为1;
总结
单例模式主要有懒汉式和饿汉式两种实现,饿汉式不会有线程安全的问题,但是提前构造对象占用了一定的资源,如果对内存要求较低的场景可以使用饿汉式实现;懒汉式应使用DCL机制来避免多线程竞争资源的问题,并且懒汉式可以在需要使用对象的时候才去创建对象,节省了资源。
属于类的创建型设计模式的一种,通过一个专门的类(工厂)来负责创建其他类的实例(具体产品),这些类都有一个共同的抽象类作为基类(抽象产品)。
简单工厂模式中的三个角色
- 工厂角色:Creator,用于创建所有的具体产品实例,是简单工厂模式的核心。工厂类直接被外部调用,并提供一个静态方法,根据传入的参数不同来创建不同产品类的实例。
- 抽象产品角色:Product,它是工厂类所创建的所有实例的类的共同基类,用于描述产品的公共接口。它和工厂类是依赖的关系(作为工厂类静态方法的返回值)。
- 具体产品角色:Concrete Product,它是抽象产品类的子类,也是工厂类要创建的目标类。
简单工厂模式实例:
假设要生产一个手机,由苹果手机,小米手机,首先应该定义一个抽象产品类PhoneInterface:
//抽象产品类
class PhoneInterface
{
public:
virtual void print_brand() = 0;
};
然后根据抽象产品类来实现具体的产品类,假设有苹果手机和小米手机:
//具体产品类
class apple : public PhoneInterface
{
public:
virtual void print_brand()
{
cout << "苹果手机" << endl;
}
};
class XiaoMi : public PhoneInterface
{
public:
virtual void print_brand()
{
cout << "小米手机" << endl;
}
};
接下来就应该创建一个工厂类,在工厂类中定义了创建所有产品类实例的逻辑,以及选择创建哪个产品类实例的判断:
//工厂类
class Factory
{
public:
PhoneInterface* production_phone(int flag)
{
PhoneInterface* pTemp = NULL;
switch (flag) //所有的生产都集中在一个工厂中,每次修改都要在类中修改,不符合开闭原则
{
case 1:
pTemp = new apple;
break;
case 2:
pTemp = new XiaoMi;
break;
default:
pTemp = NULL;
break;
}
return pTemp;
}
};
这样,客户就可以通过工厂类来生产客户所需要的手机,并可以成功完成手机型号的生产。
int main()
{
Factory* pF = NULL;
PhoneInterface* pPhone = NULL;
pF = new Factory;
pPhone = pF->production_phone(1);
pPhone->print_brand();
delete pPhone;
pPhone = pF->production_phone(2);
pPhone->print_brand();
delete pF;
delete pPhone;
system("pause");
return 0;
}
简单工厂模式的优缺点:
优点:工厂类是整个简单工厂模式的核心,通过工厂类对外隐藏了创建实例的具体细节,用户直接使用工厂类去创建自己需要的实例,而不必关心实例是如何创建出来的,也不必关心内部结构是怎么组织的。
缺点:简单工厂模式的优点来源于工厂类,其缺点也来源于工厂类,因为所有实例的创建逻辑都集中在工厂类中,一旦工厂出现问题,所有实例的创建都将无法进行,并且增删产品都要去修改工厂类来实现,不符合开闭原则。因为简单工厂模式不符合开闭原则,所以它不是标准的设计模式。
工厂模式对简单工厂模式不遵守开闭原则这一缺点做了修正,工厂模式多出了一个抽象工厂角色作为接口,实际的生产工作在具体工厂类中实现,这样进一步的抽象化使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。简单来说,就是把简单工厂中的工厂细分为不同产品的工厂,每个工厂生产一种产品。
- 抽象工厂角色(Creator),所有具体工厂都要实现这个接口;
- 具体工厂(Concrete Creator),负责实例化具体产品对象;
- 抽象角色(Product),和简单工厂模式一样,它是工厂类所创建的所有实例的类的共同基类,用于描述产品的公共接口。
- 具体产品角色(Concrete Product),具体工厂类所要实例化的对象。
工厂模式实例:
简单工厂中的抽象产品类和具体产品类不变 ;
首先定义一个抽象的工厂类,来定义具体工厂的统一接口:
//抽象工厂类
class FactoryInterface
{
public:
virtual PhoneInterface* production_phone() = 0;
};
然后定义一个苹果手机工厂,用于生产苹果手机,再定义一个小米手机工厂,用于生产苹果手机,那么这两个工厂就是具体工厂角色
//苹果手机工厂
class AppleFactory : public FactoryInterface
{
public:
virtual PhoneInterface* production_phone()
{
return new apple;
}
};
//小米手机工厂
class XiaomiFactory : public FactoryInterface
{
public:
virtual PhoneInterface* production_phone()
{
return new XiaoMi;
}
};
当我们需要生产手机产品的时候,直接使用苹果手机工厂去创建苹果手机对象,用小米手机工厂去创建小米手机对象即可。假如,我们增加需求,需要华为手机,这时只要增加一个华为工厂和华为手机的实现类即可,工厂的抽象类和手机的抽象类都不用动,符合开闭原则。
int main()
{
FactoryInterface* pFactory = NULL;
PhoneInterface* pPhone = NULL;
//要生产一台苹果手机
//先创建一个苹果手机工厂
pFactory = new AppleFactory;
pPhone = pFactory->production_phone();
pPhone->print_brand();
delete pPhone;
delete pFactory;
//生产一个小米手机
pFactory = new XiaomiFactory;
pPhone = pFactory->production_phone();
pPhone->print_brand();
delete pPhone;
delete pFactory;
system("pause");
return 0;
}
简单工厂与工厂模式对比:
简单工厂模式把所有的创建逻辑都放在了一个工厂类中,而工厂模式则是提供了一个抽象的工厂接口,由具体工厂去创建产品实例,极大的方便了增加产品,删除产品等操作,且很好的符合了开闭原则,简单工厂模式每次增删产品都要直接修改工厂类,而工厂模式只需要增加一个具体工厂类和一个具体产品类就可以完成功能的扩充。
在简单工厂模式中,客户端是面向具体工厂编程,增加产品要在工厂类中修改代码;而工厂模式是面向抽象工厂编程,增加产品只要创建一个新的具体工厂就可以了,这就是面向抽象类编程。
抽象工厂模式可以创建一个产品族(包含多条产品线)。抽象工厂模式用官方语言描述就是,一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。 她也有四种角色,和工厂模式一样。
- 抽象工厂角色(Creator),所有具体工厂都要实现这个接口,可以创建多个不同等级的产品(多条产品线);
- 具体工厂(Concrete Creator),负责实例化具体产品对象,多条产品线;
- 抽象角色(Product),和简单工厂模式一样,它是工厂类所创建的所有实例的类的共同基类,用于描述产品的公共接口。
- 具体产品角色(Concrete Product),具体工厂类所要实例化的对象。
抽象工厂模式实例:
首先创建一个抽象手机类
//抽象产品
class PhoneInterface
{
public:
virtual void print_brand() = 0;
};
根据抽象手机类定义大陆版苹果手机类,美版苹果手机类,大陆版小米手机类,美版小米手机类
//美版的苹果手机
class UsApple : public PhoneInterface
{
public:
virtual void print_brand()
{
cout << "美版的苹果手机" << endl;
}
};
//大陆版小米手机
class ChinaXiaomi : public PhoneInterface
{
public:
virtual void print_brand()
{
cout << "大陆版本的小米手机" << endl;
}
};
//美版的小米手机
class UsXiaomi : public PhoneInterface
{
public:
virtual void print_brand()
{
cout << "美版的小米手机" << endl;
}
};
接下来定义一个抽象工厂类,该抽象工厂类中包含两个接口,一个是苹果手机生产线,一个是小米手机生产线
//抽象工厂
class FactoryInterface
{
public:
//产品线1:苹果手机
virtual PhoneInterface* production_apple() = 0;
//产品线2:小米手机
virtual PhoneInterface* production_xiaomi() = 0;
};
根据抽象工厂来定义两个具体工厂,一个是大陆版手机工厂,一个是美版手机工厂
//生产大陆版本手机的工厂
class ChinaFactory : public FactoryInterface
{
public:
//产品线1:苹果手机
virtual PhoneInterface* production_apple()
{
return new ChinaApple;
}
//产品线2:小米手机
virtual PhoneInterface* production_xiaomi()
{
return new ChinaXiaomi;
}
};
//生产美版手机的工厂
class UsFactory : public FactoryInterface
{
public:
//产品线1:苹果手机
virtual PhoneInterface* production_apple()
{
return new UsApple;
}
//产品线2:小米手机
virtual PhoneInterface* production_xiaomi()
{
return new UsXiaomi;
}
};
客户可以直接使用两个具体工厂去生产大陆版苹果/小米手机或美版苹果/小米手机
int main()
{
FactoryInterface* pFactory = NULL;
PhoneInterface* pPhone = NULL;
//使用大陆版手机的工厂
cout << "======大陆版手机工厂======" << endl;
pFactory = new ChinaFactory;
//生产大陆版苹果手机
pPhone = pFactory->production_apple();
pPhone->print_brand();
delete pPhone;
//生产大陆版小米手机
pPhone = pFactory->production_xiaomi();
pPhone->print_brand();
delete pPhone;
delete pFactory;
//使用美版手机的工厂
cout << "======美版手机工厂======" << endl;
pFactory = new UsFactory;
pPhone = pFactory->production_apple();
pPhone->print_brand();
delete pPhone;
pPhone = pFactory->production_xiaomi();
pPhone->print_brand();
delete pPhone;
delete pFactory;
system("pause");
return 0;
}
总结:
当增加一个新的产品族时,只需要增加一个新的具体工厂即可,如果整个产品族只有一个等级的产品,比如只有一条生产线,抽象工厂就和工厂模式一样了。
建造者模式用于创建具有多个部件的复合对象,并隐藏了复合对象的创建过程,不同的部件建造者(Builder生成器)有不同的建造方法。通过建造者模式实现了对象的构建和对象的表示的分离,也就是说,通过同样的构建过程(建造逻辑)可以创建出不同的表示(使用不同的建造者产生不同的建造方式)。
建造者模式中的4种角色:
- 抽象建造者角色Builder:为建造各个组件提供统一的抽象接口;
- 具体建造者角色ConcreteBuilder:实现抽象建造者提供的抽象接口,定义各个组件的建造方法,是组件建造的具体实施者;
- 指挥者Director:调用具体建造者来建造产品的各个组件,指挥者并不知道产品的具体信息,指挥者只负责规定并保证各个组件的建造过程和建造逻辑(指挥建造的过程,比如先装发动机再装轮子);
- 产品角色Product:被建造的复杂对象,包含组合对象的各个部件;
建造者模式实例:
**产品角色Product:**首先我们定义一个汽车产品类,这个汽车包含外壳、发动机、车轮三个部件,并且汽车产品类中应该包含设置各个部件和获取各个部件的方法。汽车产品类是我们最终要建造的目标,是客户的需求。
//最终产品类:汽车
class CarProduct
{
public:
void set_shell(string shell) //建造汽车外壳
{
this->shell = shell;
}
void set_engine(string engine) //建造汽车发动机
{
this->engine = engine;
}
void set_whell(string whell) //建造汽车轮子
{
this->whell = whell;
}
//获取属性
string get_shell()
{
return this->shell;
}
string get_engine()
{
return this->engine;
}
string get_whell()
{
return this->whell;
}
private:
string shell; //外壳
string engine; //发动机
string whell; //轮子
};
**抽象建造者角色Builder:**定义一个抽象的建造者基类,类中统一了建造部件的接口和返回产品成品的方法。
//建造者基类:抽象施工单位
class Builder
{
public:
virtual void builder_shell() = 0; //汽车外壳的建造方式接口
virtual void builder_engine() = 0; //发动机的建造方式
virtual void builder_whell() = 0; //车轮的建造方式
virtual CarProduct* get_car() = 0; //返回建造好的汽车产品
};
**具体建造者角色ConcreteBuilder:**定义具体建造者类,具体建造者类是产品部件的具体建造者,也就是汽车的生产商,我们定义两个汽车生产商,一个厂家生产卡车,一个厂家生产火车。
//具体的建造者:具体施工单位、具体的建造方式
class TruckBuilder : public Builder //卡车建造商
{
private:
CarProduct* m_car;
public:
TruckBuilder()
{
this->m_car = new CarProduct;
}
virtual void builder_shell()
{
this->m_car->set_shell("卡车外壳");
}
virtual void builder_engine()
{
this->m_car->set_engine("卡车发动机");
}
virtual void builder_whell()
{
this->m_car->set_whell("卡车轮子");
}
virtual CarProduct* get_car()
{
return this->m_car;
}
};
class TrainBuilder : public Builder //火车建造商
{
private:
CarProduct* m_car;
public:
TrainBuilder()
{
this->m_car = new CarProduct;
}
virtual void builder_shell()
{
this->m_car->set_shell("火车外壳");
}
virtual void builder_engine()
{
this->m_car->set_engine("火车发动机");
}
virtual void builder_whell()
{
this->m_car->set_whell("火车轮子");
}
virtual CarProduct* get_car()
{
return this->m_car;
}
};
**指挥者Director:**最后,应该定义一个指挥者,指挥者是汽车的设计师,它负责规划建造汽车的逻辑步骤,指挥汽车厂家(具体建造者)干活,而具体的工作有汽车厂家去干,所以指挥者不关心汽车的具体细节,只负责设计建造汽车各个部件的逻辑关系,比如先制造汽车外壳,然后安装发动机,最后安装车轮。
//指挥者:设计师,负责设计建造逻辑
class Director
{
public:
Director(Builder* builder)
{
this->m_builder = builder;
}
//建造逻辑
void builder_logic()
{
//1.先建造车的外壳
this->m_builder->builder_shell();
cout << "先建造车的外壳\t";
//2.再安装发动机
this->m_builder->builder_engine();
cout << "再安装发动机\t";
//3.最后安装车轮
this->m_builder->builder_whell();
cout << "最后安装车轮\n";
}
private:
Builder* m_builder;
};
最后是根据客户需求去建造汽车,假设客户需要一辆卡车。
int main()
{
CarProduct* myCar = NULL;
Builder* tempBuilder = NULL;
Director* carDector = NULL;
//需求:建造一辆卡车
//首先找一个卡车建造商
tempBuilder = new TruckBuilder;
//把建造商交给指挥者(设计师)管理
carDector = new Director(tempBuilder);
//开始建造
carDector->builder_logic();
//获取产品 对象的建造逻辑和产品的表示分离
myCar = tempBuilder->get_car();
cout << "======产品信息======" << endl;
cout << myCar->get_shell() << endl;
cout << myCar->get_engine() << endl;
cout << myCar->get_whell() << endl;
cout << "====================" << endl;
delete myCar;
delete carDector;
delete tempBuilder;
}
假如客户提出新需求,需要一辆火车,那么直接让指挥者去指挥火车厂商生产即可。
//新需求:需要一辆火车
tempBuilder = new TrainBuilder;
carDector = new Director(tempBuilder);
carDector->builder_logic();
myCar = tempBuilder->get_car();
cout << "======产品信息======" << endl;
cout << myCar->get_shell() << endl;
cout << myCar->get_engine() << endl;
cout << myCar->get_whell() << endl;
cout << "====================" << endl;
delete myCar;
delete carDector;
delete tempBuilder;
建造者模式和工厂模式对比:
建造者模式和工厂模式的区别是:工厂模式强调的是结果,不考虑对象的建造过程,只关注产生一个客户所需要的结果。比如,客户需要一辆大众汽车,那么就直接使用大众汽车工厂来生产一辆大众汽车,只关注大众汽车这个结果,不关心汽车外壳、发动机、轮子等部件的建造过程。建造者模式强调的是建造过程,要关注每一个部件的建造方式,以及各个部件的建造逻辑,最终组合出需要的对象。
原型模式是一种对象创建型模式,它采取复制原型对象的方法来创建对象的实例,所以称之为Clone,被复制出来的对象具有和原型一摸一样的数据,并且在通过Clone创造另一个一模一样的对象时,不需要知道创造的过程。根据对象克隆深度层次的不同,有浅度克隆与深度克隆。
原型模式实例:
既然原型模式是复制一个一模一样的对象,那么就一定要注意潜在的深拷贝浅拷贝问题。
#include <iostream>
using namespace std;
#include <String>
class MyString
{
public:
virtual MyString* Clone() = 0;
virtual void print_str() = 0;
};
class Hello : public MyString
{
private:
int len;
string str;
public:
Hello()
{
this->len = 5;
this->str = "Hello";
}
virtual MyString* Clone()
{
Hello* temp = new Hello; //这里是在堆上new出来的指针,因此不会发生悬吊
*temp = *this;
return temp;
}
virtual void print_str()
{
cout << "len:" << len << " str:" << str << endl;
}
};
int main()
{
MyString* h1 = new Hello;
h1->print_str();
MyString* h2 = h1->Clone();
h2->print_str();
delete h1;
delete h2;
system("pause");
return 0;
}
代理模式,是构造型的设计模式之一,它可以为其他对象提供一种代理来控制对这个对象的访问。所谓的代理,就是指一个具有与被代理对象(代理元)相同接口的类,客户端只有通过Proxy来实现与被代理类的交互,并且在交互过程中 ,代理可以增加其它操作。
代理可以分为多个种类:
- 远程代理:可以隐藏一个对象在不同地址空间的事实,可以理解为将工作委托给远程的代理(服务器)来完成;
- 虚拟代理:通过代理来存放需要很长时间实例化的对象;
- 安全代理:用来控制真实对象的访问权限;
- 智能引用:当调用真实对象时,代理处理另外一些事
在代理模式中,有四种角色:
- 抽象主题角色subject:真实主题与代理主题的共同接口,并供客户端使用;
- 真实主题角色RealSubject: 定义了代理角色所代表的真实实体。
- 代理主题角色Proxy:含有一个引用,使得代理可以访问真实主题角色,并提供一个和抽象主题角色相同的接口,以便于代理角色可以用来替代真是主题角色。代理角色通常在将客户端调用传递给真实主题对象之前或者之后执行某些操作,而不是单纯返回真实的对象,也就是说代理可以执行真实主题角色之外的其他操作。
代理模式实例:
假设现在有一个客户需要买车,他可以去实体店买,也可以在瓜子二手车买,瓜子二手车就是一个代理,它可以销售实体店的车,并提供节假日打折活动。
#include <iostream>
using namespace std;
//抽象主题角色
class CarStore
{
public:
virtual void sail_Car() = 0;
};
//真实主题角色
class ReilCarStore : CarStore //实际汽车销售店铺
{
public:
virtual void sail_Car()
{
cout << "实体店铺直接卖出汽车" << endl;
}
};
//代理
class GZproxy : public CarStore //瓜子二手车是一个代理商
{
public:
/*
virtual void sail_Car()
{
cout << "瓜子二手车直营(自己卖)" << endl;
}
*/
virtual void sail_Car()
{
ReilCarStore cartore;
cartore.sail_Car(); //表面是瓜子二手车卖车,实际上销售的是实体店的车
discount(); //扩充了实体店卖车的形式和功能
}
void discount()
{
cout << "双11活动打折" << endl;
}
};
//客户
int main()
{
GZproxy dd;
dd.sail_Car();
system("pause");
return 0;
}
装饰模式,也叫做包装模式,是结构型模式的一种。装饰模式动态的给一个对象增加了额外的功能,并且这种扩充功能的方式对客户是透明的。装饰模式的具体实现就是把一些功能封装在一个个单独的子类中,并让这些子类包含要被装饰的对象,当有需要功能扩充的时候,客户就可以有选择的通过装饰类来装饰某个对象。装饰模式可以理解为继承的一种替代,他比继承更加灵活,客户可以根据需要自由选择。
- Component:被装饰的主体,定义了一个对象接口,可以给这些对象增加装饰功能;
- ConcreteComponent:具体的对象,继承于Component;
- Decorator:装饰者,继承于Component并从Component类的外部完成对Component类功能的扩充,并且Component类并不需要知道装饰者的存在;
装饰模式实例:
Component:
假设我们要设计一个游戏角色Hero,它最初只有跑步的技能,但是通过升级可以完成功能扩充,比如飞行,发射激光等。
首先定义一个Component角色,也就是要被装饰的主题的抽象接口。
class Hero
{
public:
virtual void show_skill() = 0;
};
ConcreteComponent:
然后定义一个具体的要被装饰的对象ConcreteComponent
//初始的英雄:只能跑
class runHreo : public Hero
{
public:
void run_skill()
{
cout << "只能跑的超人" << endl;
}
virtual void show_skill()
{
run_skill();
}
};
Decorator:
最后添加两个装饰者,每个装饰者都可以完成对被装饰对象不同的功能扩充,一个装饰者可以为英雄角色扩充飞行技能,另一个装饰者可以为英雄角色扩充激光发射功能
//超人升级:新增了飞行技能
class flyHero : public Hero
{
private:
Hero* m_hero;
public:
flyHero(Hero* hero)
{
this->m_hero = hero;
}
void fly_skill()
{
cout << "拥有飞行技能" << endl;
}
virtual void show_skill()
{
this->m_hero->show_skill();
fly_skill();
}
};
//超人升级:可以发射激光
class laserHero : public Hero
{
private:
Hero* m_hero;
public:
laserHero(Hero* hero)
{
this->m_hero = hero;
}
void laser_skill()
{
cout << "可以发射激光" << endl;
}
virtual void show_skill()
{
this->m_hero->show_skill();
laser_skill();
}
};
最后是客户需求,首先我们让一个英雄角色从跑步到飞行再到激光发射,一步步的获取技能
int main()
{
//创建一个超人角色
Hero* myHero1 = NULL;
cout << "*********第一个英雄*********" << endl;
//初始功能只有跑
cout << "======初始形态======" << endl;
myHero1 = new runHreo;
myHero1->show_skill();
//功能增强:升级飞行技能
cout << "======第一次升级======" << endl;
Hero* myHero2 = new flyHero(myHero1);
myHero2->show_skill();
//再次升级:可以放大招
cout << "======第二次升级======" << endl;
Hero* myHero3 = new laserHero(myHero2);
myHero3->show_skill();
delete myHero1;
delete myHero2;
delete myHero3;
system("pause");
return 0;
}
构造型模式之一,也叫做变压器模式和装饰模式都是一种包装模式(Wrapper),通过适配器模式可以改变现有类的接口形式。适配器模式可以将一个类的接口转换成客户希望的另一种形式的接口,使得原本因接口不兼容而无法工作的类可以一起工作,适用于双方都不适合修改的场景。
- Target:客户期待的接口,可以是抽象类或者接口;
- Adapter:适配器,对Target和Adaptee进行适配,通过在内部包装一个Adaptee对象,来把源接口转换成目标接口;
- Adaptee:适配者,也就是需要被适配的类;
适配器模式实例:
假设现在只有一个RS232标准的接口,但是客户需求一个TTL标准的接口,这时就可以通过一个适配器把RS232接口适配成TTL电平标准。
#include <iostream>
using namespace std;
//Target:客户需要一个TTL电平接口
class TTL
{
public:
virtual void get_ttl() = 0;
};
//Adaptee:现在只有一个RS232接口
class RS232
{
public:
void get_rs232()
{
cout << "RS232接口" << endl;
}
};
//Adapter:适配器
class adapter : public TTL
{
private:
RS232* m_232;
public:
adapter(RS232* m_232)
{
this->m_232 = m_232;
}
void adapter232_to_ttl()
{
cout << "适配器:将RS232转换为TTL" << endl;
}
virtual void get_ttl()
{
this->m_232->get_rs232();
adapter232_to_ttl();
cout << "ttl电平接口" << endl;
}
};
int main()
{
RS232* my232 = NULL;
adapter* ad = NULL;
//现有一个RS232电平接口
my232 = new RS232;
//需求是TTL接口,所以创建一个适配器
my232->get_rs232();
cout << "=========" << endl;
ad = new adapter(my232);
ad->get_ttl();
delete my232;
delete ad;
system("pause");
return 0;
}
组合模式将对象组合成树形结构以表示“整体-部分”的层次结构,并使得用户对单个对象和组合对象的使用具有一致性。并且通过一个属性对象可以可以访问整棵树的对象。
组合模式的三种角色:
- Component:抽象角色,代表树形结构的抽象结点,它定义了所有实现类的统一接口(属性、方法、行为),并提供了访问和管理子结点的接口;简言之,Component定义了Leaf和Composite共同的操作接口,比如增加子结点,删除子结点等行为,是一个抽象基类。
- Leaf:叶子结点,叶子结点中没有子结点,类似于树中的叶子结点,或文件系统中的文件。
- Composite :枝结点,可以存储子结点,并实现子结点的操作,类似于文件系统的文件夹。
组合模式实例:
Component
首先定义一个抽象类,为Leaf和Composite提供统一接口,假设要做一个文件系统,文件夹(Composite)中可以放入文件和文件夹,文件(Leaf)中不可以放入任何子结点。
class Component
{
public:
virtual void display() = 0; //显示当前文件或文件夹名称
virtual void add(Component* node) = 0; //在当前文件夹增加一个文件或文件夹
virtual void remove(Component* node) = 0; //在当前文件夹删除一个文件或文件夹
virtual vector<Component*>* get_child() = 0; //获取文件夹下属文件或文件夹
};
Leaf
定义一个文件类,文件中不可加入子结点
class Leaf : public Component
{
private:
string name;
public:
Leaf(string name)
{
this->name = name;
}
virtual void display()
{
cout << "Leaf: " << this->name << endl;
}
virtual void add(Component* node)
{
cout << "叶子结点,无法加入" << endl;
}
virtual void remove(Component* node)
{
cout << "叶子结点,无此操作" << endl;
}
virtual vector<Component*>* get_child()
{
cout << "叶子结点,无子结点" << endl;
return NULL;
}
};
Composite
定义一个文件夹类,可以加入问价或文件夹
class Composite : public Component
{
private:
string name;
vector<Component*>* vec;
public:
Composite(string name)
{
this->name = name;
vec = new vector<Component*>;
}
~Composite()
{
if (vec != NULL)
{
delete vec;
vec = NULL;
}
}
virtual void display()
{
cout << "Composite: " << this->name << endl;
}
virtual void add(Component* node)
{
vec->push_back(node);
}
virtual void remove(Component* node)
{
for (vector<Component*>::iterator it = vec->begin(); it != vec->end(); it++)
{
if (*it == node)
{
vec->erase(it);
}
}
}
virtual vector<Component*>* get_child()
{
cout << "*" << this->name << " child: " << "*\n";
for (vector<Component*>::iterator it = vec->begin(); it != vec->end(); it++)
{
(*it)->display();
}
return vec;
}
};
客户端操作
int main()
{
Component* root = NULL;
Leaf* l1 = NULL;
Leaf* l2 = NULL;
Composite* dir = NULL;
root = new Composite("/root");
l1 = new Leaf("1.cpp");
l2 = new Leaf("2.cpp");
dir = new Composite("/home");
root->add(dir);
dir->add(l1);
dir->add(l2);
cout << "============" << endl;
root->display();
root->get_child();
dir->get_child();
cout << "============" << endl;
delete dir;
delete l2;
delete l1;
delete root;
system("pause");
return 0;
}
桥接模式基于类的最小设计原则,通过使用封装,聚合以及继承等行为来让不同的类承担不同的责任。它的主要特点是把抽象(abstraction)与行为实现(implementation)分离开来,从而可以保持各部分的独立性以及应对它们的功能扩展。也就是说,通过桥接模式可以实现抽象部分与实现部分的解耦合,使得抽象和实现都可以独立的发生变化,当通过继承不能实现开闭原则的时候,就可以考虑桥接模式。比如,图形和颜色,把图形设计一个抽象类,颜色设计一个抽象类,然后根据需要的图形去实现图形类,并根据需要的颜色实现颜色类,通过两个抽象类就可以实现颜色和图形的组合。
也就是说,通过桥接模式可以实现抽象部分与实现部分的解耦合,使得抽象和实现都可以独立的发生变化,当通过继承不能实现开闭原则的时候,就可以考虑桥接模式。比如,图形和颜色,把图形设计一个抽象类,颜色设计一个抽象类,然后根据需要的图形去实现图形类,并根据需要的颜色实现颜色类,通过两个抽象类就可以实现颜色和图形的组合。
- Abstractions:抽象类接口,包含了一个对行为实现类Implementor的引用,也就是说二者具有关联关系。
- Refined Abstraction:抽象类接口的实现类,实现了Abstractions中定义的接口,并可以调用Implementor中的方法(包含了Implementor的引用)。
- Implementor:行为实现类接口,定义了一系列操作。
- Concretelmp lementor:具体实现类,实现了Implementor中的操作。
桥接模式实例:
实现对不同图形上不同颜色:
Abstractions
抽象图形类
class Graph
{
protected:
Color* mGraphColor;
public:
Graph(Color* mGraphColor)
{
this->mGraphColor = mGraphColor;
}
virtual void smear_color() = 0; //给图形上色
};
Refined Abstraction
实现具体的图形
class Circle : public Graph
{
public:
Circle(Color* mGraphColor) : Graph(mGraphColor) {};
virtual void smear_color()
{
cout << "圆形 + ";
mGraphColor->get_color();
}
};
class Triangle : public Graph
{
public:
Triangle(Color* mGraphColor) : Graph(mGraphColor) {};
virtual void smear_color()
{
cout << "三角形 + ";
mGraphColor->get_color();
}
};
Implementor
抽象颜色类
class Color
{
public:
virtual void get_color() = 0;
};
Concretelmp lementor
定义三个具体的颜色
class Red : public Color
{
public:
virtual void get_color()
{
cout << "红色" << endl;
}
};
class Blue : public Color
{
public:
virtual void get_color()
{
cout << "蓝色" << endl;
}
};
class Yellow : public Color
{
public:
virtual void get_color()
{
cout << "黄色" << endl;
}
};
客户端操作,给图形上色
int main()
{
Color* m_color = NULL;
m_color = new Red;
Circle* m_circle = new Circle(m_color);
m_circle->smear_color();
delete m_color;
m_color = new Blue;
Triangle* m_triangle = new Triangle(m_color);
m_triangle->smear_color();
delete m_color;
delete m_triangle;
delete m_circle;
system("pause");
return 0;
}
外观模式为一个子系统集合提供了一个一致的简单界面,并且外观包含了对各个子系统的引用,客户端可以通过这个外观来访问各个子系统。
外观模式中的角色:
- Facade:外观类,作为调用接口,提供所有子系统的一致访问界面;
- Clients:通过外观角色来访问各个子系统;
- Subsystem:子系统,实现子系统的功能;
外观模式实例:
Subsystem
首先创建子系统集合,假设为电脑系统,定义显示屏子系统、主机子系统、键盘子系统
class Screen //子系统1
{
public:
void get_attr()
{
cout << "显示屏子系统" << endl;
}
};
class Host //子系统2
{
public:
void get_attr()
{
cout << "主机子系统" << endl;
}
};
class Keyboard //子系统3
{
public:
void get_attr()
{
cout << "键盘子系统" << endl;
}
};
如果客户不适应外观类,而是直接访问这个子系统,那么操作将会比较繁琐,要创建每个子系统对象,并逐个调用其方法来访问子系统。
void Func1()
{
Screen* mScreen;
Host* mHost;
Keyboard* mKeyboard;
mScreen = new Screen;
mHost = new Host;
mKeyboard = new Keyboard;
mScreen->get_attr();
mHost->get_attr();
mKeyboard->get_attr();
}
Facade
创建一个外观类,它包含对所有子系统的引用
class Facade
{
private:
Screen* mScreen;
Host* mHost;
Keyboard* mKeyboard;
public:
Facade()
{
mScreen = new Screen;
mHost = new Host;
mKeyboard = new Keyboard;
}
~Facade()
{
if (mScreen != NULL)
{
delete mScreen;
}
if (mHost != NULL)
{
delete mHost;
}
if (mKeyboard != NULL)
{
delete mKeyboard;
}
}
void get_attr()
{
this->mScreen->get_attr();
this->mHost->get_attr();
this->mKeyboard->get_attr();
}
};
通过外观来访问所有子系统
void Func2()
{
Facade* f = new Facade;
f->get_attr();
delete f;
}
在面向对象系统的设计和实现中,创建对象是最常见的操作,这里就会有一个问题:如果一个应用程序使用了太多对象,就会造成很大的存储开销。特别是对于大量轻量级(细粒度)的对象,比如在文档编辑器的设计过程中,我们如果为每一个字母创建一个对象的时候可能会因为大量的对象而造成存储开销的浪费,例如一个字母a在文档中出现了10000次,而实际上我们可以让这10000个a共享一个对象,这种情况下,我们可以将对象状态分为内部状态和外部状态。在享元模式中可以共享(不会变化)的状态称为内部状态(Intrinsic State),而那些需要外部环境来设置的不能共享的内容称为外部状态(Extrinsic State),其中外部状态和内部状态是相互独立的,外部状态的变化不会引起内部状态的变化。内部状态直接存储在对象中,而外部状态(字母a的大小、字体)可以在适当时机作为参数传递给对象。享元模式的目的就是使用共享技术来实现大量细粒度对象的复用。
享元模式的角色:
- Flyweight:抽象享元角色,定义了所有具体享元类的接口,通过这个接口可以传入外部状态并作用于外部状态;
- FlyweightFactory:享元工厂,创建并管理享元对象;
- ConcreteFlyweight:可共享的内部状态;
- UnsharedConcreteFlyweight:可以不共享的享元子类;
Flyweight
假设一个字符串和一个标志作为一对,可以通过标志来确定字符串,比如只要传入标志1就得到字符串hello,也就是说,通过flag可以确定一个字符串。
class MyStr
{
protected:
string str;
public:
MyStr(string str)
{
this->str = str;
}
virtual void get_str() = 0;
};
ConcreteFlyweight
string str为可共享的内部状态;
UnsharedConcreteFlyweight
不共享的享元子类
class Hello : public MyStr
{
private:
int id;
public:
Hello(string str, int id) : MyStr(str)
{
this->id = id;
}
virtual void get_str()
{
cout << "id: " << id << " 对应的str: " << str << endl;
}
};
FlyweightFactory
创建一个工厂
class Factory
{
private:
map<int, Hello*> m;
public:
~Factory()
{
while (!m.empty())
{
Hello* tmp = NULL;
map<int, Hello*>::iterator it = m.begin();
tmp = it->second;
m.erase(it);
delete tmp;
}
}
Hello* get_str(int id)
{
Hello* mtemp;
map<int, Hello*>::iterator it;
it = m.find(id);
if (it == m.end())
{
string temp;
cout << "该字符串未找到,请输入字符串:";
cin >> temp;
mtemp = new Hello(temp, id);
m.insert(make_pair(id, mtemp));
return mtemp;
}
else
{
return it->second;
}
}
};
客户端通过工厂获取享元对象,并传入一个标志
int main()
{
Hello* h1 = NULL, * h2 = NULL;
Factory* f = NULL;
f = new Factory;
h1 = f->get_str(1);
h1->get_str();
h2 = f->get_str(1);
h2->get_str();
delete h2;
delete h1;
delete f;
system("pause");
return 0;
}
通过模板模式可以把特定步骤的算法接口定义在抽象基类中,通过子类继承对抽象算法进行不同的实现来达到改变算法行为的目的。通俗来讲就是,在抽象类中定义好算法步骤并统一接口,在子类中实现接口,这就实现了算法操作步骤和算法实现的解耦合。模板模式一般应用于,具有同样的操作步骤,但是这些操作的细节不同的场景。
- AbstractClass:定义了算法的框架和步骤;
- ConcreteClass:实现AbstractClass中的方法;
AbstractClass
我们把穿衣服看做一个固定流程,先穿外套,再穿裤子,最后穿鞋
class WearClothes //穿衣服
{
public:
virtual void wear_coat() = 0;
virtual void wear_pants() = 0;
virtual void wear_shoe() = 0;
public:
void wear_order() //穿衣服的顺序已经提前确定好了---模板
{
wear_coat(); //先穿外套
wear_pants(); //再穿裤子
wear_shoe(); //最后穿鞋
}
};
ConcreteClass
然后定义穿睡衣,穿西装两个类,穿衣服的顺序都是固定的,但是穿的衣服有所不同
class WearSuit : public WearClothes //穿西装
{
virtual void wear_coat()
{
cout << "穿西服外套" << endl;
}
virtual void wear_pants()
{
cout << "穿西服裤子" << endl;
}
virtual void wear_shoe()
{
cout << "穿小皮鞋" << endl;
}
};
class WearPajamas : public WearClothes //穿睡衣
{
virtual void wear_coat()
{
cout << "穿睡衣" << endl;
}
virtual void wear_pants()
{
cout << "穿睡裤" << endl;
}
virtual void wear_shoe()
{
cout << "穿拖鞋" << endl;
}
};
最后客户端执行穿衣服操作
int main()
{
WearClothes* wear = NULL;
//穿西装应酬
wear = new WearSuit;
wear->wear_order();
delete wear;
//穿睡衣睡觉
wear = new WearPajamas;
wear->wear_order();
delete wear;
system("pause");
return 0;
}
命令模式就是把命令对象、命令的创建者、命令的调用者、命令的执行者(接收者)分离,首先看一个命令模式中的四个角色:
- Command:抽象命令,定义了操作的接口,把命令封装成一个类,通过继承在ConcreteCommand中来实现具体的命令操作;
- ConcreteCommand:具体命令,实现抽象命令的接口,是被命令调用者Invoker调用的主体,并且包含了一个对命令接收者Receiver的引用;
- Receiver:命令的执行者,收到命令后执行相应的操作;
- Invoker:命令的调用者,客户创建命令,并通过 Incoker 去调用命令,Invoker包含了对Command的引用,并负责去调用命令中的操作;
Receiver
以办理银行业务为例,首先创建一个银行职员作为命令接收者,他可以执行存款取款操作。
//Receiver
class Banker
{
public:
void saving_money()
{
cout << "办理存款业务" << endl;
}
void withdraw_money()
{
cout << "办理取款业务" << endl;
}
};
Command
创建一个命令抽象类。
class Command
{
public:
virtual void conduct_business() = 0;
};
ConcreteCommand
通过继承实现一个存款命令类和一个取款命令类,这两个具体命令方法分别可以调用命令执行者的存款操作和取款操作。
class SaveCommand : public Command
{
private:
Banker* bker;
public:
SaveCommand(Banker* bker)
{
this->bker = bker;
}
virtual void conduct_business()
{
this->bker->saving_money();
}
};
class WithdrowCommand : public Command
{
private:
Banker* bker;
public:
WithdrowCommand(Banker* bker)
{
this->bker = bker;
}
virtual void conduct_business()
{
this->bker->withdraw_money();
}
};
Invoker
创建一个命令调用者,它可以调用命令对象(命令调用者Invoker中包含了命令Command的引用,它可以调用命令,而具体命令ConcreteCommand中包含了命令接收者Receiver的引用,具体命令可以调用Receiver的操作,客户通过命令调用者Invoker来命令Receiver进行相应操作)。
//Invoker
class Manager
{
private:
Command* com;
public:
Manager(Command* com)
{
this->com = com;
}
void order()
{
com->conduct_business();
}
};
最后客户端创建命令,并通过Invoker来调用命令,使Receiver执行相应操作
int main()
{
Manager* m = NULL;
Command* com = NULL;
Banker* bker = NULL;
bker = new Banker;
com = new SaveCommand(bker); //存款命令
m = new Manager(com);
m->order();
delete com;
delete m;
com = new WithdrowCommand(bker); //取款命令
m = new Manager(com);
m->order();
delete m;
delete com;
delete bker;
system("pause");
return 0;
}
责任链模式就像一个链表,将对象连成一个链式结构,并沿着这条链传递请求,直到请求被某个对象处理。在责任链模式中,客户端只要把请求放到对象链上即可,不需关心请求的传递过程和处理细节,实现了请求发送和请求处理的解耦合。
- Handler:抽象处理者,定义了处理请求的接口,并包含一个指向下一个对象的指针;
- :具体处理者,负责处理请求或把请求沿着对象链传递给下一个具体处理者;
Handler
定义抽象处理者
class Handler
{
protected: //供子类使用
Handler* next;
public:
virtual void perform_task() = 0; //统一的任务接口
Handler* set_next(Handler* next) //设置下一个要执行的任务
{
this->next = next;
return this->next;
}
};
ConcreteHandler
具体处理者
class Task1 : public Handler
{
public:
virtual void perform_task()
{
cout << "任务 1 执行" << endl;
if (next != NULL) //如果有下一个任务,则执行
{
next->perform_task();
}
}
};
class Task2 : public Handler
{
public:
virtual void perform_task()
{
cout << "任务 2 执行" << endl;
if (next != NULL)
{
next->perform_task();
}
}
};
class Task3 : public Handler
{
public:
virtual void perform_task()
{
cout << "任务 3 执行" << endl;
if (next != NULL)
{
next->perform_task();
}
}
};
客户端发出请求
int main()
{
Handler* task1 = NULL;
Handler* task2 = NULL;
Handler* task3 = NULL;
task1 = new Task1;
task2 = new Task2;
task3 = new Task3;
//任务流程:task1 -> task2 -> task3 -> 结束
cout << "任务流程:task1 -> task2 -> task3 -> 结束" << endl;
task1->set_next(task2);
task2->set_next(task3);
task3->set_next(NULL);
task1->perform_task();
cout << "===================================" << endl;
//改变流程
cout << "任务流程:task3 -> task2 -> task1 -> 结束" << endl;
task1->set_next(NULL);
task2->set_next(task1);
task3->set_next(task2);
task3->perform_task();
cout << "===================================" << endl;
delete task3;
delete task2;
delete task1;
system("pause");
return 0;
}
策略模式可以定义一个算法族,把一系列算法封装起来并提供一个统一接口,这样算法之间的切换或其他变化不会影响客户端。其关键在于,把算法的抽象接口封装在一个类中,算法的实现由具体策略类来实现,,而算法的选择由客户端决定。
- Strategy:抽象策略类,定义算法族的统一接口;
- ConcreteStrategy:具体策略类,实现了具体的算法操作;
- Context:上下文,包含一个策略类的引用,根据不同策略执行不同操作,策略的选择由客户端决定;
Strategy
定义一个排序算法抽象策略类
class Strategy //策略
{
public:
virtual void sort() = 0;
};
ConcreteStrategy
实现具体的算法操作
class SelectSort : public Strategy
{
public:
virtual void sort()
{
cout << "选择排序算法" << endl;
}
};
class InsertSort : public Strategy
{
public:
virtual void sort()
{
cout << "插入排序算法" << endl;
}
};
Context
class Context
{
private:
Strategy* m_strategy;
public:
void set_strategy(Strategy* m_strategy)
{
this->m_strategy = m_strategy;
}
void execute_strategy()
{
this->m_strategy->sort();
}
};
客户端选择具体排序算法
int main()
{
Strategy* s1 = NULL;
Context* c = NULL;
c = new Context;
cout << "========================" << endl;
//使用选择排序算法进行排序
cout << "使用选择排序算法进行排序" << endl;
s1 = new SelectSort;
c->set_strategy(s1);
c->execute_strategy();
delete s1;
cout << "========================" << endl;
cout << "使用插入排序算法进行排序" << endl;
s1 = new InsertSort;
c->set_strategy(s1);
c->execute_strategy();
delete s1;
delete c;
cout << "========================" << endl;
system("pause");
return 0;
}
类与类之间的交互都放在一个中介对象中进行,即类通过中介和另一个类交互,类与类之间不用互相引用就能实现交互,降低了类与类之间的耦合。但是需要通过中介者进行交互的类中包含了中介者的引用,而中介者也包含了所有需要交互的类的引用。举例来说,比如男女相亲,男生女生之间互不相识(交互双方不需互相引用),但是他们都认识媒人(交互者包含了中介者的引用),而且媒人也认识男生和女生(中介者包含了交互者的引用),再比如第三方招聘平台、招聘者、应聘者之间的关系也是这样。
- Mediator:抽象中介者,定义了同事对象到中介者对象的接口;
- ConcreteMediator:具体中介者,它包含了所有具体同事类的引用,并实现抽象中介类中的接口;
- Colleague:抽象同事类;
- ConcreteColleague:具体同事类,每个同事类只知道自己的行为,但是他们都包含中介类的引用(都认识中介);
中介者模式实例
Mediator
大学生通过BOOS直聘找工作,那么大学生和HR就需要通过BOOS直聘找工作,BOOS直聘就是中介,首先定义一个抽象中介类。
class Mediator //抽象中介者
{
public:
virtual void match() = 0;
virtual void set_hr(Role* hr) = 0;
virtual void set_student(Role* student) = 0;
};
Colleague
定义抽象同事类,也就是需要交互的类,他应该包含一个中介类的引用,因为大学生和HR都应该认识BOOS直聘平台。
class Role //抽象角色
{
protected:
string name;
string office;
Mediator* mediator;
public:
Role(string name, string office, Mediator* mediator)
{
this->name = name;
this->office = office;
this->mediator = mediator;
}
string get_name()
{
return this->name;
}
string get_office()
{
return this->office;
}
virtual void match(Role* role) = 0;
};
ConcreteMediator
定义具体的中介BOOS直聘,大学生和公司HR通过BOOS直聘求职或招聘。
class Boos : public Mediator //BOOS直聘平台
{
private:
Role* hr;
Role* student;
public:
virtual void set_hr(Role* hr)
{
this->hr = hr;
}
virtual void set_student(Role* student)
{
this->student = student;
}
virtual void match()
{
cout << "=========================" << endl;
cout << hr->get_name() << " 提供职位:" << hr->get_office() << endl;
cout << student->get_name() << " 需求职位:" << student->get_office() << endl;
if (hr->get_office() == student->get_office())
{
cout << "***匹配成功***" << endl;
}
else
{
cout << "***匹配失败***" << endl;
}
cout << "=========================" << endl;
}
};
ConcreteColleague
定义学生类和HR类
class Student : public Role
{
public:
Student(string name, string office, Mediator* mediator) : Role(name, office, mediator) {};
virtual void match(Role* role)
{
mediator->set_hr(role);
mediator->set_student(this);
mediator->match();
}
};
class HR : public Role
{
public:
HR(string name, string office, Mediator* mediator) : Role(name, office, mediator) {};
virtual void match(Role* role)
{
mediator->set_hr(this);
mediator->set_student(role);
mediator->match();
}
};
大学生和HR通过BOOS进行交互
int main()
{
Role* hr = NULL;
Role* stu1 = NULL, * stu2 = NULL;
Mediator* medi = NULL;
medi = new Boos;
hr = new HR("七总", "C++", medi);
stu1 = new Student("小明", "Java", medi);
stu2 = new Student("小红", "C++", medi);
hr->match(stu1);
hr->match(stu2);
delete stu2;
delete stu1;
delete hr;
system("pause");
return 0;
}
观察者模式提供了一种一对多的模式,多个观察者对象同时监听一个主题对象,一旦主题对象发生变化,能够自动通知所有的观察者对象。它提供了一种关联对象之间的同步机制,他们之间通过通信来保持状态同步。
- Subject:抽象主题角色,被观察的对象,当被观察的状态发生变化时,会通知所有的观察者,Subject一般包含了所有观察者对象的引用(集合);
- ConcreteSubject:具体主题,被观察者的具体实现,当状态发生改变时,向所有观察者发出通知;
- Observer:抽象观察者,提供统一的接口,在得到通知时执行某些操作;
- ConcreteObserver:具体观察者,实现抽象观察者提供的接口;
观察者模式实例
ConcreteObserver
假设两军作战,观察者为士兵
//观察者
class Soldier //士兵
{
private:
Guard* guard;
public:
Soldier(Guard* guard)
{
this->guard = guard;
}
void recv_infor(string infor)
{
cout << infor << ": 收到, 准备战斗" << endl;
}
};
ConcreteSubject
被观察者为哨兵,一旦发现敌军,则通知所有士兵,被观察者含有一个观察者类型的容器,用于保存所有观察者的引用。
class Guard //哨兵
{
private:
list<Soldier*> l;
public:
void add_observer(Soldier* soldier)
{
l.push_back(soldier);
}
void send_infor(string infor)
{
for (list<Soldier*>::iterator it = l.begin(); it != l.end(); it++)
{
(*it)->recv_infor(infor);
}
}
};
备忘录模式在不破坏封装性的前提下,捕获一个对象的内部状态,并在外部保存,并在需要的时候恢复对象以前的状态。可以理解为,在备忘录中保存了一个对象的备份,当对象已经发生了改变,并且我们需要恢复对象以前的状态时,可以通过一个备忘录管理器来恢复以前的状态。
- Originator:原生者角色,需要在备忘录中保存的对象,负责创建一个备忘录,并进行保存和恢复状态的操作;
- Memento:备忘录,用于保存Originator的内部状态;
- Caretaker:管理者,包含一个备忘录的引用,负责操作和管理备忘录;
Memento
定义一个备忘录
class Memento //备忘录
{
private:
string str;
public:
Memento(string str)
{
this->str = str;
}
string get_str()
{
return this->str;
}
};
Originator
定义一个原生者
class Originator
{
private:
string str;
public:
void set_str(string str)
{
this->str = str;
}
Memento* get_memo()
{
return new Memento(this->str);
}
void print_str()
{
cout << this->str << endl;
}
void recover(Memento* memo)
{
this->str = memo->get_str();
}
};
Caretaker
定义一个管理者
class Caretaker
{
private:
Memento* memo;
public:
void set_memo(Memento* memo)
{
this->memo = memo;
}
Memento* get_memo()
{
return this->memo;
}
};
访问者模式把数据结构和作用于数据结构上的操作进行了分离,在不修改已有类的前提下可以增加新的操作,而新增新的操作就相当于新增一个访问者。
- Visitor:抽象访问者角色,声明了访问操作的方法,方法的参数为被访问的元素;
- ConcreteVisitor:具体访问者角色,实现抽象访问者中声明的方法;
- Element:抽象元素角色,声明接受访问的操作,并接收一个访问者对象作为参数;
- ConcreteElement:具体元素角色,实现被访问的操作;
- ObjectStructure:结构对象角色,包含一个具体元素的引用的容器;
Visitor
定义一个抽象访问者类
class Element;
class Visitor //抽象访问者
{
public:
virtual void visit(Element* e) = 0;
};
Element
定义一个抽象元素类
class Element //抽象元素
{
public:
virtual void reception(Visitor* v) = 0;
virtual string get_name() = 0;
};
ConcreteElement
以领导访问公司部门为例,创建两个公司部门
class Department1 : public Element //部门1
{
public:
virtual void reception(Visitor* v)
{
v->visit(this);
}
virtual string get_name()
{
return "第一事业部";
}
};
class Department2 : public Element //部门2
{
public:
virtual void reception(Visitor* v)
{
v->visit(this);
}
string get_name()
{
return "第二事业部";
}
};
ConcreteVisitor
创建访问者,董事长和部门分管领导
class President : public Visitor //董事长
{
public:
virtual void visit(Element* e)
{
cout << "董事长访问:" << e->get_name() << endl;
}
};
class Leader1 : public Visitor //分管领导1
{
public:
virtual void visit(Element* e)
{
cout << "第一分管领导访问:" << e->get_name() << endl;
}
};
class Leader2 : public Visitor //分管领导2
{
public:
virtual void visit(Element* e)
{
cout << "第二分管领导访问:" << e->get_name() << endl;
}
};
首先在客户端实现,分管部门的领导对自己的部门进行访问
{
v1 = new Leader1;
v2 = new Leader2;
e1 = new Department1;
e2 = new Department2;
e1->reception(v1);
e2->reception(v2);
}
通过改变对象的内部状态来达到改变对象行为的目的,“这个对象表现得就好像改变了它的类一样”。其实说白了就是,根据用户是输入的条件,满足一定条件就改变对象的行为,不同条件执行不同的操作。
- State:抽象状态,定义了一个接口,接口声明了一个与上下文环境相关的状态的行为;
- ConcreteState:具体状态,定义了本状态的行为和转换到另一个状态的判定条件;
- Context:上下文、环境,负责状态的转换,包含了一个表示当前状态的State类型的引用;
State
定义抽象状态
class State
{
public:
virtual void get_state(Machine* m) = 0;
};
ConcreteState
定义具体状态
class State1 : public State
{
public:
virtual void get_state(Machine* m);
};
class State2 : public State //状态2
{
public:
virtual void get_state(Machine* m);
};
实现状态对应行为
void State1::get_state(Machine* m)
{
if (m->get_flag() == 1)
{
//当前状态标志是1,则执行状态1
cout << "执行状态1" << endl;
}
else
{
//当前状态标志不是1,则切换为状态2
//删除原来的状态
delete m->get_current();
//把当前状态设置为状态2
m->set_flag(2);
m->set_current(new State2);
//执行状态
m->get_current()->get_state(m);
}
}
void State2::get_state(Machine* m)
{
if (m->get_flag() == 2)
{
cout << "执行状态2" << endl;
}
else
{
//删除原状态
delete m->get_current();
//设置新的当前状态
m->set_flag(1);
m->set_current(new State1);
//执行当前状态
m->get_current()->get_state(m);
}
}
Context
定义一个Context类
class Machine
{
private:
State* current; //当前状态
int flag; //状态标志
public:
State* get_current()
{
return this->current;
}
void set_current(State* s)
{
this->current = s;
}
void set_flag(int flag)
{
this->flag = flag;
}
void execute_state()
{
current->get_state(this);
}
int get_flag()
{
return flag;
}
};
客户端实现状态行为的执行和转换
int main()
{
Machine* m = NULL;
State* s = NULL;
m = new Machine;
cout << "======初始状态======" << endl;
//初始化为状态1
s = new State1;
m->set_flag(1);
m->set_current(s);
m->execute_state();
cout << "======切换为状态2======" << endl;
//切换为状态2
m->set_flag(2);
m->execute_state();
cout << "======切换为状态1======" << endl;
//切换为状态1
m->set_flag(1);
m->execute_state();
delete s;
delete m;
system("pause");
return 0;
}
解释器模式提供一种对自定义语句的解释机制,解释器模式包含以下几种角色:
- Context:解释器的上下文环境,包含了不属于解释器的其他信息;
- AbstractExpression:抽象解释器,定义了一个抽象的解释操作接口;
- ConcreteExpression:具体解释器,实现对相关操作的解释;
Context
定义上下文环境
class Context
{
private:
int data;
int ret;
public:
void set_data(int data)
{
this->data = data;
}
void set_ret(int ret)
{
this->ret = ret;
}
int get_data()
{
return this->data;
}
int get_ret()
{
return this->ret;
}
};
AbstractExpression
定义抽象解释器类
class Expression
{
protected:
Context* context;
public:
virtual void interpret(Context* context) = 0;
};
ConcreteExpression
实现一个加法解释器,加法解释器的操作是对传入的数据执行+1操作
class PlusExpression : public Expression
{
virtual void interpret(Context* context)
{
int temp = context->get_data();
temp++;
context->set_ret(temp);
}
};
迭代器模式提供了一种从外部遍历访问一个容器的方法,并且在不需知道容器内部细节的前提下就可以完成对容器的顺序遍历。所以,创建迭代器的容器应该将自身的引用(this指针)传递给迭代器,迭代器通过持有的这个容器的引用来实现对容器的遍历。
- Iterator:迭代抽象类,用于提供实现迭代的最小方法集,一般包括获取开始对象、获取下一个对象、获取当前对象、判断是否结束这几个方法;
- ConcreteIterator:具体的迭代器,实现抽象迭代器定义的方法;
- Aggregate:聚集抽象类,可以理解为一个容器的接口;
- ConcreteAggregate:具体聚集类,容器的实现类;
Iterator
定义一个抽象的迭代器类
class Iterator
{
public:
virtual void first() = 0;
virtual void next() = 0;
virtual bool is_done() = 0;
virtual int current_item() = 0;
};
Aggregate
定义抽象容器类
class Aggregate
{
public:
virtual Iterator* create_iterator() = 0;
virtual int get_item(int index) = 0;
virtual int get_size() = 0;
};
ConcreteIterator
实现一个int类型的迭代器
class IntIterator : public Iterator
{
private:
Aggregate* age;
int index;
public:
IntIterator(Aggregate* age) //迭代器应该持有一个创建迭代器的容器的引用,这样才能通过迭代器访问容器
{ //谁创建迭代器就把谁的引用传递给迭代器
this->age = age;
this->index = 0;
}
virtual void first()
{
index = 0;
}
virtual void next()
{
if (index < age->get_size())
{
index++;
}
}
virtual bool is_done()
{
if (index == age->get_size())
{
return true;
}
return false;
}
virtual int current_item()
{
return age->get_item(index);
}
};
ConcreteAggregate
定义一个int类型的容器
class IntArray : Aggregate
{
private:
int size;
int* array;
public:
IntArray(int size)
{
this->size = size;
array = new int[size];
for (int i = 0; i < size; i++)
{
array[i] = i + 1;
}
}
~IntArray()
{
if (array != NULL)
{
delete array;
array = NULL;
}
this->size = 0;
}
virtual Iterator* create_iterator()
{
return new IntIterator(this); //把自己的引用传给迭代器
}
virtual int get_item(int index)
{
return array[index];
}
virtual int get_size()
{
return this->size;
}
};
客户端,通过迭代器来遍历数组
int main()
{
Iterator* it = NULL;
IntArray* array = NULL;
array = new IntArray(10);
it = array->create_iterator();
cout << "遍历数组:";
for (; !(it->is_done()); it->next())
{
cout << it->current_item() << " ";
}
cout << endl;
system("pause");
return 0;
}