感觉很难写,因为都是看了《大话设计模式》的,感觉讲得很行,对于要学习设计模式的基础的人来说,很是很好入门的,我这里再写,总感觉有点照抄的感觉,不写的话,总感觉学习的不够激情,那就只抓重点出来写写,再加上自己的理解,就是这样。
2.1 简单工厂模式
2.1.1 定义
简单工厂模式属于创建型模式又叫做静态工厂方法模式,它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。
简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
2.1.2 UML图
简单工厂类:简单工厂模式的核心,负责创建所需的具体产品类,传入的参数就是有简单的标识即可。
运算类:抽象类,工厂模式创建所有对象的父类,负责描述所有实例类的所共有的属性。
减法类、加法类、乘法类,除法类:具体实例类,是简单工厂模式的创建目标。
2.1.3 简单工厂模式实现
抽象运算类:
//抽象运算类
class operator1
{
public:
double numberA;
double numberB;
void set(double numberA, double numberB) {
this->numberA = numberA;
this->numberB = numberB;
}
virtual double GetResult() = 0;
};
具体运算类:
//具体实现类
//加法类:
class add : public operator1
{
virtual double GetResult() {
return numberA + numberB;
}
};
//减法类:
class sub : public operator1
{
virtual double GetResult() {
return numberA - numberB;
}
};
//乘法类:
class multi : public operator1
{
virtual double GetResult() {
return numberA * numberB;
}
};
//除法类:
class div1 : public operator1
{
virtual double GetResult() {
return numberA / numberB;
}
};
创建工厂类:
通过这个类中的静态方法,创建具体的对象,然后使用这个对象去调用方法。
//简单工厂类
class Factory
{
public:
static operator1 *creatoperator(string flag) {
if(flag == "+") { //判断是创建哪个对象
return new add;
} else if(flag == "-") {
return new sub;
} else if(flag == "*") {
return new multi;
} else if(flag == "/") {
return new div1;
}
}
};
调用:
//使用
int main()
{
Factory *factory = new Factory();
operator1 *opera = factory->creatoperator("+");
opera->set(10, 20);
cout << "结果: " << opera->GetResult() << endl;
delete opera;
opera = factory->creatoperator("-");
opera->set(10, 20);
cout << "结果: " << opera->GetResult() << endl;
delete opera;
opera = factory->creatoperator("*");
opera->set(10, 20);
cout << "结果: " << opera->GetResult() << endl;
delete opera;
opera = factory->creatoperator("/");
opera->set(10, 20);
cout << "结果: " << opera->GetResult() << endl;
delete opera;
return 0;
}
2.1.4 优点
- 客户端和具体的类实现了解耦。
- 对于某些对象创建过程比较复杂的情况,我们不用考虑这些了。
- 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
2.1.5 缺点
- 这个类的职责过重,这个类如果发生问题,会影响很多实用这个工厂的模块。
- 使用简单工厂模式将会增加系统中类的个数,在一定程序上增加了系统的复杂度和理解难度。
- 简单工厂模式,增加新的功能是通过修改源代码实现的,不符合开闭原则。
- 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。
2.1.6 适用环境
- 工厂类负责创建的对象比较少,由于创建的对象少,不会造成工厂方法中的业务逻辑太过复杂。
- 客户端只知道传入工厂类的参数,对于如何创建对象并不关心。
2.1.7 模式应用
- JDK类库中广泛使用了简单工厂模式,如工具类java.text.DateFormat,它用于格式化一个本地日期或者时间。
public final static DateFormat getDateInstance();
public final static DateFormat getDateInstance(int style);
public final static DateFormat getDateInstance(int style,Locale locale);
- 获取不同加密算法的密钥生成器。
KeyGenerator keyGen=KeyGenerator.getInstance("DESede");
2.2 工厂方法模式
由于简单工厂模式存在一些问题,简单工厂类职责过重,并且在要增加新的具体类的实现,需要直接修改代码,这有违开闭原则。所以在简单工厂的模式下,又改进了一点,形成了一个新的模式:工厂方法模式。
2.2.1 定义
定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。
2.2.2 UML图
工厂类:抽象类,所有工厂类的抽象,可以通过createoperator来创建产品。
加法工厂、减法工厂、乘法工厂、除法工厂:具体类,是把工厂类具体实例化,完成具体产品的创建。
运算类:抽象类,工厂模式创建所有对象的父类,负责描述所有实例类的所共有的属性。
减法类、加法类、乘法类,除法类:实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
2.2.3 工厂方法模式实现
抽象运算类:
//抽象运算类
class operator1
{
public:
double numberA;
double numberB;
void set(double numberA, double numberB) {
this->numberA = numberA;
this->numberB = numberB;
}
virtual double GetResult() = 0;
};
具体实现类:
//具体实现类
//加法类:
class add : public operator1
{
virtual double GetResult() {
return numberA + numberB;
}
};
//减法类:
class sub : public operator1
{
virtual double GetResult() {
return numberA - numberB;
}
};
//乘法类:
class multi : public operator1
{
virtual double GetResult() {
return numberA * numberB;
}
};
//除法类:
class div1 : public operator1
{
virtual double GetResult() {
return numberA / numberB;
}
};
工厂类:
//工厂类
class Factory1
{
public:
virtual operator1 *creatoperator() = 0;
};
具体的工厂类:
//具体的工厂类
//加法工厂
class AddFactory : public Factory1
{
public:
operator1 *creatoperator() {
return new add;
}
};
//减法工厂
class SubFactory : public Factory1
{
public:
operator1 *creatoperator() {
return new sub;
}
};
//乘法工厂
class MultiFactory : public Factory1
{
public:
operator1 *creatoperator() {
return new multi;
}
};
//除法工厂
class DivFactory : public Factory1
{
public:
operator1 *creatoperator() {
return new div1;
}
};
客户端:
//工厂方法模式
Factory1 *factory1 = new AddFactory(); //如果需要换方法,就修改这个就可以了
opera = factory1->creatoperator();
opera->set(10, 20);
cout << "结果: " << opera->GetResult() << endl;
delete opera;
2.2.4 优点
- 不需要记住具体的类名,甚至连具体参数都不用记忆
- 实现了对象创建和使用分离
- 系统的可扩展性也就变得非常好,无需修改接口和原类。
2.2.5 缺点
- 增加系统中类的个数,复杂度和理解度增加
- 增加了系统的抽象性和理解难度
2.2.6 适用环境
- 客户端不知道它所需要的对象的类
- 抽象工厂类通过其子类来指定创建哪个对象
2.2.7 使用场景
1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。
2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。 3、设计一个连接服务器的框架,需要三个协议,“POP3”、“IMAP”、“HTTP”,可以把这三个作为产品类,共同实现一个接口。
2.3 抽象工厂模式
怎么还有一个抽象工厂,这个工厂模式真多,不过其实思想也差不多的,下面了解一波。
2.3.1 定义
是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无需指定所要产品的具体类就能得到同族的不同等级的产品的模式结构
2.3.2 UML图
这个图跟工厂方法模式差不多,只不过抽象工厂类只有一个创建类的方法,这个抽象工厂需要创建一组类。
抽象工厂接口:一个抽象类,负责创建产品。
SQL、Access:具体的工厂类。
user类:抽象的产品类,只要是负责用户的创建的基类。
SQLUser、AccessUser:具体的用户创建的类。
Department类:第二个抽象产品类,也是负责用户创建基类。
SQLDepartment、AccessDepartment:具体的用户创建的类。
2.3.3 抽象工厂模式实现
user类:
//user类
class User
{
public:
virtual void insert() = 0;
virtual void getUser() = 0;
};
//sqluser类
class SQLUser : public User
{
public:
virtual void insert() {
cout << "sql user 插入" << endl;
}
virtual void getUser() {
cout << "sql user 获取" << endl;
}
};
//accessuser类
class AccessUser : public User
{
public:
virtual void insert() {
cout << "Access User 插入" << endl;
}
virtual void getUser() {
cout << "Access User 获取" << endl;
}
};
department类:
//department类
class department
{
public:
virtual void insert() = 0;
virtual void getDepartment() = 0;
};
//sqldepartment类
class SQLDepartment : public department
{
public:
virtual void insert() {
cout << "SQL Department 插入" << endl;
}
virtual void getDepartment() {
cout << "SQL Department 获取" << endl;
}
};
//Accessdepartment类
class AccessDepartment : public department
{
public:
virtual void insert() {
cout << "Access Department 插入" << endl;
}
virtual void getDepartment() {
cout << "Access Department 获取" << endl;
}
};
抽象工厂接口:
//抽象工厂接口
class AbFactory
{
public:
virtual User* creatUser() = 0;
virtual department* creatDepartment() = 0;
};
//SQL
class SQL : public AbFactory
{
public:
virtual User* creatUser() {
return new SQLUser;
}
virtual department* creatDepartment() {
return new SQLDepartment();
}
};
//Access
class Access : public AbFactory
{
public:
virtual User* creatUser() {
return new AccessUser;
}
virtual department* creatDepartment() {
return new AccessDepartment;
}
};
客户端:
//使用
int main()
{
AbFactory *abf = new SQL; //如果修改数据库,就修改这里
User *iu = abf->creatUser();
iu->insert();
iu->getUser();
department *id = abf->creatDepartment();
id->insert();
id->getDepartment();
return 0;
}
2.3.4 优点
-
易于交换产品系列,由于具体工厂类,在一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易,它只需要改变具体工厂即可使用不同的产品配置。
-
它让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户代码中。
2.3.5 缺点
- 如果要增加一个项目表Project,那么就需要增加三个类,IProject,MySQLProject,OracleProject,还需要改动AbFactory,SQL,Access才可以完全实现。这样是很糟糕的。
2.3.6 改进
正因为有上面的缺点,一改就改好几个类,好累,所以需要改进,改进的地方,就是使用简单工厂模式,把创建的工厂类全部整合到一个类中,例:
class DataAccess
{
public:
static User* creatUser(string db) {
if(db == "SQL") {
return new SQLUser;
} else if(db == "Access") {
return new AccessUser;
}
}
static department* creatDepartment(string db) {
if(db == "SQL") {
return new SQLDepartment;
} else if(db == "Access") {
return new AccessDepartment;
}
}
};
就是把判断逻辑都放到一个类中处理,客户端代码:
string db = "SQL";
iu = DataAccess::creatUser(db);
iu->insert();
iu->getUser();
id = DataAccess::creatDepartment(db);
id->insert();
id->getDepartment();
也是可以完成胜任的。
不过这样子也不是最好的,string db = “SQL”;这句话也是在编译的时候编译进去的,如果需要修改,也要直接修改代码,不过这样修改的地方就少了很多。
我们还可以通过读取参数文件的方式,把这个用的数据库读取到变量中,这样就可以做到不需要修改代码。
2.3.7 使用场景
1、QQ 换皮肤,一整套一起换。
2、生成不同操作系统的程序。
2.4 单例模式
大名鼎鼎的单例模式,终于到了,知道的第一个设计模式就是单例模式,用处还挺多的,这也是最后一个创建型模式了,创建型就是说是创建类的类型这种模式。
2.4.1 定义
这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
2.4.2 UML图
2.4.3 饿汉式
//饿汉式
class Singleton
{
private:
Singleton() {}
private:
static Singleton *single;
public:
static Singleton* getInstance() {
return single;
}
};
Singleton *Singleton::single = new Singleton;
饿汉式比较简单,因为没有多线程问题,因为这个single已经在程序初始化的时候初始化了,这时候,多线程还没启用,所以用GetInstance这个函数获取的对象永远都是一个。
2.4.4 懒汉式
static pthread_mutex_t g_mutex_lock;
//懒汉式
class Singleton1
{
private:
Singleton1() {}
private:
static Singleton1 *single;
public:
static Singleton1* getInstance() {
if(single == NULL) { //提高性能,不用每次都判断互斥锁
pthread_mutex_lock(&g_mutex_lock); //上锁
if(single == NULL) { //判断是否真的需要申请
single = new Singleton1;
}
pthread_mutex_unlock(&g_mutex_lock); //开锁
}
return single;
}
};
//记得初始化静态变量
Singleton1 *Singleton1::single = NULL;
//使用
int main()
{
pthread_mutex_init(&g_mutex_lock, NULL);
Singleton1 *s1 = Singleton1::getInstance();
Singleton1 *s2 = Singleton1::getInstance();
if(s1 == s2) {
cout << "相等" << endl;
}
return 0;
}
懒汉式就是麻烦点:
- 记得静态变量需要初始化
- 记得线程安全(多线程的时候需要加锁)
- 记得提高性能,每次都判断是否上锁,也消化性能
2.4.5 优点
-
在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
-
避免对资源的多重占用(比如写文件操作)。
2.4.6 缺点
-
不适用于变化频繁的对象;
-
滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;
-
如果实例化的对象长时间不被利用,系统会认为该对象是垃圾而被回收,这可能会导致对象状态的丢失;
2.4.7 使用场景
- 要求生产唯一序列号。
- WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
- 创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。