实用的设计模式【一】---类设计法则

如何专业的定义一个class?这里记录一下自己的checklist

1 关于构造函数

a. 考虑声明为explicit

避免一个参数或多个参数从第二个后都有默认值下的隐式调用。

一个参数的构造函数(或者除了第一个参数外其余参数都有默认值的多参构造函数), 承担了两个角色。 1 是个构造器 ,2 是个默认且隐含的类型转换操作符。所以, 有时候在我们写下如 AAA = XXX, 这样的代码, 且恰好XXX的类型正好是AAA单参数构造器的参数类型, 这时候编译器就自动调用这个构造器, 创建一个AAA的对象。

b. 考虑禁用复制构造函数、赋值函数

基于三大件”法则,一旦实现了析构,则复制构造、赋值构造得同时考虑,比如含有成员指针下的浅copy问题;c++中,默认对于类会产生以下成员函数(如果你没有定义的话,这通常是不可控的)。这里取址函数不用多说,关键是赋值相关的两个函数,默认是浅copy的,所以还是禁用,参考析构函数注意点

 1 class Empty() {
 2         public:
 3                 Empty();
 4                 ~Emtpy();                                   
 5 
 6                 Empty* operator&();                  //取址运算符
 7                 const Empty* operator&() const;      //取址运算符 const
 8 
 9         private:  
10                 Empty(const Empty &);                //赋值构造函数
11                 Empty& operator=(const Empty &);     //赋值运算符
12 }

c. 构造函数可以抛出异常,但没有返回值;考虑单独实现一个Init函数作为补充

2 采用初始化列表

有两种情况你必须这样做:

a.对于的const \ reference的成员

b.派生类的方法中需要调用基类的构造函数

Base(int x):m_x(x) {
  //base constructor } Derived(int x) : Base(x) {
  //derived constructor }

3 成员函数如果不改变类的状态,声明时后加const

注意const函数只能访问const函数,而const对象只能访问const函数

4 尽可能将成员变量private更可控,更透明

5 以非member 方法代替需要多个对象的member组合功能的实现

6 考虑Law of Demeter,适当做一个权衡,少一些wrapper或中介类

7 关于析构函数

a. 一个类要被继承时(其往往含有虚函数要多态),要提供虚析构函数

否则存在下面的情况:

    Base *pb = new Derived();
    delete pb;//这里就不会调用派生类的析构函数,尽管pb的虚指针指向了派生的虚表,但其虚表中没有虚析构函数,造成派生对象资源泄露

b.只要有基类的析构函数是虚拟的,那么所有的派生类不管是否明确的写了虚拟析构函数,派生了的析构函数可以认为一定是虚拟的。如同类的虚指针,所有派生的默认都有

c.在析构中不要抛出exception,否则实现此异常接口

如果类含有指针成员和引用成员,则该类型通常需要实现析构函数(这里不一定是虚的,但是需要考虑清理内存)
更进一步,一旦实现了析构函数,就进而需要实现实现拷贝构造函数与拷贝复制函数,否则,需要明确拒绝(或者,仅仅一个理由:做副本有没有道理,见这里第一个条款)

8 LSP法则

一个类是否可以设计为另一个类的子类?该原则指出在不修改任何行为的前提下,用派生类替换基类——应该总是可行的。比如我们不能将圆形定义为椭圆的子类、不能把正方形定义为长方形的子类。

除了私有继承(不暴露出原来的共有方法)外,可以使用组合来解决之——将原来计划的父类声明为当前类的一个成员(可以是指针、引用),在通用方法内用该成员调用其对应并不适配的方法。

class Circle
{
public:
    Circle();
    explicit Circle(float r);

    //这个方法只有圆形有,椭圆没有
    //这里仍然是构建在椭圆的功能之上,但是并没有使得epllise的接口暴露在circle中
    void SetRadius(float r) {
        _ellipse.SetXRadius(r);
        _ellipse.SetYRadius(r);
    }
private:
    Ellipse _ellipse;
}

9 开放-封闭法则

类的目标应该是为扩展开放的,为修改而关闭的。比如本博客《面向实用的设计模式》中提到的简单工厂与工厂对象而言。

但是,实际这个OCP难以实现,我们只需要尽可能做到:维护一个稳定的上层接口API,但是接口内部的实现可以改变。

10 Demeter法则

翻译为迪米特法则,又叫最少知识原则,用以降低耦合。意味着一个函数只可以做下面的事情:

- 调用同一个类的其它函数

- 在同一个类的数据成员上调用函数

- 在它接受的任何参数上调用函数

- 在它创建的任何局部对象上调用函数

- 在全局对象上调用函数(绝不应该有全局变量)

可以得到,不能通过另外一个函数调用获取对象上的调用函数,如ObjA.GetObjB().DoAction()。化解的方法是,要么重构A对象,要么将B对象作为函数入参传入后再调用。

其它请参考:http://www.cnblogs.com/leby/p/5008036.html

 

posted @ 2015-04-11 20:26  lebyzhao  阅读(401)  评论(0编辑  收藏  举报