VC++编程之道读书笔记(2)
第三篇 技术细节
第七章:细说开发人员必知必会的39个开发细节
细节36:单例模式的应用
在开发程序时,往往需要在整个工程中只需要一个类的实例。而这个实例一旦被创建就不能被其他的实例再创建了,通常我们称这个实现过程为单例模式。
既然要保证类只有一个实例,那么就需要其他的类不能使用实例化该类。因此,需要将其构造方法设为私有的,即使用private关键字修饰。同时,类中提供一个静态方法,该方法的返回值是该类的一个实例。这样就只能使用该静态方法来获取类的实例了,从而保证了唯一性。
下面通过具体代码来实现一个单例模式的应用,代码如下:
1 #include <iostream> 2 using namespace std; 3 4 class Emperor 5 { 6 private: 7 static Emperor *pEmperor; // declear a reference 8 static int count; // contructor times 9 Emperor() // contructor 10 { 11 count++; 12 } 13 public: 14 static Emperor getInstance() 15 { 16 if(NULL == pEmperor) 17 { 18 pEmperor = new Emperor(); 19 } 20 21 return *pEmperor; 22 } 23 void getName() 24 { 25 cout << "I am the " << count << "contructor" << endl; 26 } 27 } 28 int main(void) 29 { 30 cout << "The first time contructor" << endl; 31 Emperor emperor1 = Emperor::getInstance(); 32 emperor1.getName(); 33 34 cout << "The second time contructor" << endl; 35 Emperor emperor2 = Emperor::getInstance(); 36 emperor2.getName(); 37 38 cout << "The third time contructor" << endl; 39 Emperor emperor3 = Emperor::getInstance(); 40 emperor3.getName(); 41 42 return 0; 43 }
要想实现单例模式,首先,需要将类的构造方法定义为类的私有成员方法。当然,如果不是实现单例模式,这样做是不对的,因为在类外是无法创建该类的实例的。既然无法创建类的实例,那么单例模式又是如何创建这个类的实例的呢?方法很简单,在类中定义一个公有的静态成员方法,在这个静态成员方法中创建类的实例就可以了。
细节37:策略模式的简单应用
当我们在解决问题时,这个问题的解决方案有很多种,处理起来非常不方便。例如,在使用图像处理软件处理图片后,需要选择一种格式保存。然而各种格式在底层实现的算法并不相同,这刚好适合策略模式。
对于策略模式,需要定一个抽象类来标识各种策略的抽象。这样就可以使用多态来让虚拟机选择不同的实现类。然后让每一个中具体的策略来实现这个抽象,并为其中定义的方法提供具体的实现。由于在选择适当的策略上有些不方便,需要不断地判断需要的类型,因此用简单工厂方法类实现判断过程。
下面通过代码来看一下策略模式的应用,代码如下:
1 #include <iostream> 2 3 using namespace std; 4 5 const int GIF = 1; 6 const int JPEG = 2; 7 // 抽象类 8 class ImageSaver 9 { 10 public: 11 virtual void save() = 0; // 纯虚函数 12 }; 13 14 class GIFSaver : public ImageSaver 15 { 16 public: 17 virtual void save() 18 { 19 cout << "将图片保存成GIF格式" << endl; 20 } 21 }; 22 23 class JPEGSave : public ImageSaver 24 { 25 pbulic: 26 virtual void save() 27 { 28 cout << "将图片保存成JPEG格式" << endl; 29 } 30 }; 31 32 class TypeChooser 33 { 34 public: 35 static ImageSaver* getSaver(int type) 36 { 37 if(type == GIF) 38 { 39 return new GIGSaver(); 40 } 41 else if(type == JPEG) 42 { 43 return new JPEGSaver(); 44 } 45 else 46 { 47 return null; 48 } 49 } 50 }; 51 52 int main(void) 53 { 54 cout << "用户选择了GIF格式:" << endl; 55 ImageSaver *saver = TypeChooser::getSaver(GIF); // 获得保存图片为GIF类型的对象 56 saver->save(); 57 cout << "用户选择了JPEG格式:" << endl; 58 delete saver; 59 saver = TypeChooser::getSaver(JPEG); 60 saver->saver(); 61 62 return 0; 63 }
在上面的代码中定义了接口ImageSaver,在该接口中定义了Save方法。接下来编写类GIFSaver类和JPEGSaver类,这两个类实现了ImageSaver接口。在实现save()方法时将图片保存成GIF和JPEG格式。编写类TypeChooser,该类根据用户提供的图片类型来选择合适的图片存储方式。这样就实现了一个简单的策略模式的应用。同时也使用了简单工厂模式。
细节38:适配器模式的使用
对于刚从工厂中生产出来的产品,有些功能并不能完全满足用户的需要。因此用户通常会对其进行一定的改装工作。在不破坏原有产品的情况下为其添加新的功能,这时就需要使用适配器模式。
适配器模式可以在符合OCP原则(开放封闭原则)的基础上,为类增加新的功能。该模式涉及的主要角色有以下几点。
- 目标角色:就是期待得到的类,例如本实例的GPS抽象类。
- 源角色:需要被增加功能的类,例如本实例的Car类。
- 适配器角色:新创建的类,在源角色的基础上实现了目标角色,例如本实例的GPSCar类。
关于各个类的继承关系,如图1-1所示:
下面通过具体的实例来进一步了解适配器模式的应用,代码如下:
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 typedef char String[30]; 7 struct Point 8 { 9 int x, y; 10 }; 11 12 class Car 13 { 14 private: 15 String name; 16 double speed; 17 public: 18 double getSpeed() 19 { 20 return speed; 21 } 22 void setSpeed(double sp) 23 { 24 this->speed = sp; 25 } 26 char *getName() 27 { 28 return name; 29 } 30 void setName(String strName) 31 { 32 strcpy(this->name, strName); 33 } 34 public: 35 virtual void toString() 36 { 37 cout << "车名:" << name << ", " 38 << "速度:" << speed << "千米/小时" << endl; 39 } 40 }; 41 class GPS 42 { 43 public: 44 virtual Point getLocation() = 0; // 纯虚函数 45 }; 46 47 class GSPCar : public Car, GPS 48 { 49 public: 50 Point getLocation() 51 { 52 Point point; 53 point.x = getSpeed(); 54 point.y = getSpeed(); 55 56 return point; 57 } 58 void toString() 59 { 60 Car::toString(); 61 cout << "坐标:(" << getLocation().x << ", " << getLocation().y << ")" << endl; 62 } 63 }; 64 65 int main(void) 66 { 67 cout << "自定义普通的汽车" << endl; 68 Car car; 69 car.setName("Audi"); 70 car.setSpeed(100); 71 car.toString(); 72 73 cout << "自定义GPS汽车" << endl; 74 GPSCar gpsCar; 75 gpsCar.setName("BMW"); 76 gpsCar.setSpeed(120); 77 gpsCar.toString(); 78 79 return 0; 80 }
在上面的代码中产品是有Car类所标识的小汽车,新增加的功能是有GPS类标识的GPS定位功能。然后有适配器类GPSCar将Car类和GPS类组合到一起形成一个新的产品,这一过程就是适配器模式的使用。