您的位置:首页 > 编程语言 > C语言/C++

学习 Policy based design - 读C++设计新思维-泛型编程与设计模式的应用

2013-12-23 00:32 1706 查看
现在将今年读过的一些书, 感觉比较有心得的地方做一些分享.

久闻Andrei, Alexandrescu的Modern C++ Design - Generic Programing and Design Patterns Applied是一本比较难的书,今年将其读了一遍, 确实如此, 大家如果要卒读的话,还是有一些建议:

1) 个人感觉最有用, 最核心的思想是前3章 - 基于Policy的Class 设计, 一些技术Techniques, 以及Type lists, 其中Policy based design尤为重要,可以精读理解.

2) 第4-第11章是作者以自己的思想重新实现常见的设计模式. 个人感觉现代C++的库(Boost及其他库) 有更简洁和更常用的方式,可以粗读了解一下.

3) 如果你工作在一些大型的,注重设计的C++ Code base上, 还是不妨细读一下全书大部分章节,比如Visitors, Multimethods, 因为往往可以碰见以模板, policy实作的程序框架, 可以在扩展和修改代码的时候心里更有底.

下面来讲一讲Policy based design, 其实这个东西是多继承和模板优点的集合体. 注意到以下几点问题:

1) 多重继承可以帮助处理设计组合, 但有一些问题: 基类没有足够派生类的类型信息, 如果要操作相同的状态, 那么这个状态必须被一个虚基类持有,并由子类们虚拟继承. 修改状态就是修改基类细节, 复杂, 没有弹性.

2) 模板无法特化数据成员, 也无法对多个模板参数的模板类的成员函数进行部分特化.

3) 多继承缺乏类型信息, 模板有的是信息, 模板难于扩张(特化) ,多继承很容易扩张. 多继承的继承结构难于套用, 模板很容易被套用.

那么, 组合这两个东西, 就得到了一套类似于Strategy设计模式的,在编译期作用的技术.

template <
class RunPolicy,
class FlyPolicy
>
class Bird : public RunPolicy, public FlyPolicy
{
};

class BirdOO : public IRunnable, public IFlyable
{
};
简单是吧, Bird就是Policy的Host, 要想发挥policies的威力, 应该注意:

1) 在设计的时候, 逐步将Class里的Policies分离出来, 支持"可扩充的行为", 和"优雅的机能削减".

2) 设计一簇小Policies类的时候,尽量让Policies彼此间正交, 解除彼此的依赖.

给出一个例子来说明Policy建模的威力:

需求:我们来做一个手机跑分和信息软件:

1) 支持查询主流手机的部件信息: CPU, 内存, 显示屏, 电池.

2) 支持对手机进行Benchmark, 实际是对其子部件运行一些程序进行Benchmark.

3) 能修改某个手机的部件(改款), 也很容易快速加入新的手机进行Benchmark.

先对CPU,制作出一些类, 每个类中自己实现自己的Benchmark:

struct MTK6589
{
static void BenchMark_4Core()
{
std::cout << "MTK 6589 Score : 12000" << std::endl;
}
};

struct SnapDragonS800
{
static void BenchMark_4Core()
{
std::cout << "Snap dragon s800 Score : 22000" << std::endl;
}
};

struct A6
{
static void BenchMark_2Core()
{
std::cout << "Apple A6 Score : 20000" << std::endl;
}
};


接下来实作出屏幕,电池部分:

struct HD
{
static void Display()
{
std::cout << "1280x720" << std::endl;
}
};

struct FHD
{
static void Display()
{
std::cout << "1920x1080" << std::endl;
}
};

struct AppleRetina
{
static void Display()
{
std::cout << "1136x640" << std::endl;
}
};
struct ReplacableBattery
{
static void Switch()
{
std::cout << "Switch the battery" << std::endl;
}
};

struct FixedBattery
{
};
为了组装我们的智能手机, 我们来实作一些Policy类, 双核, 四核, 单核 处理器, OLED, LCD屏幕, 可更换/不可更换的电池策略:

template <class CPU> struct QuadCorePolicy
{
void BenchMark()
{
CPU::BenchMark_4Core();
}
};

template <class CPU> struct DuelCorePolicy
{
void BenchMark()
{
CPU::BenchMark_2Core();
}
};

template <class CPU> struct SingleCorePolicy
{
void BenchMark()
{
CPU::BenchMark_1Core();
}
};
template <class DISPLAY> struct OLEDDisplayPolicy
{
void DisplayOLED()
{
DISPLAY::Display();

//write your oled code here
}
};

template <class DISPLAY> struct LCDDisplayPolicy
{
void DisplayLCD()
{
DISPLAY::Display();

//write your lcd code here
}
};
template <class BATTERY> struct ReplacableBatteryPolicy
{
void SwitchBattery()
{
BATTERY::Switch();
}
};

template <class BATTERY> struct FixedBatteryPolicy
{

};
有了这些Policies, 我们可以实作出对应的Host, 就是我们的智能手机类:

template <class CPU, class DISPLAY, class BATTERY,
template<class C> class NUMCorePolicy = QuadCorePolicy,
template<class D> class DisplayPolicy = LCDDisplayPolicy,
template<class B> class BatteryPolicy = ReplacableBatteryPolicy >
class SmartPhone : public NUMCorePolicy<CPU>
, public DisplayPolicy<DISPLAY>
, public BatteryPolicy<BATTERY>
{

};
看上去是复杂了些, SmartPhone类使用了3个子部件的模板参数, 3个Policies.

有了这个智能手机模板,我们就开始生产2013年的风云手机吧, 暂时拿iPhone5, 小米红米手机, Samsung Galaxy Note3来做例子:(在我心中,小米是可以与苹果三星一战的哦)

void producePhones()
{
typedef SmartPhone<A6, AppleRetina, FixedBattery, DuelCorePolicy, LCDDisplayPolicy > AppleIPhone5;

AppleIPhone5 iphone5;

iphone5.BenchMark();
iphone5.DisplayLCD();

//Iphone could switch a battery?
//iphone5.SwitchBattery();

typedef SmartPhone<MTK6589, HD, ReplacableBattery, QuadCorePolicy, LCDDisplayPolicy > XiaomiRedMi;

XiaomiRedMi redmi;

redmi.BenchMark();
redmi.DisplayLCD();

//Oh, I am run off of my battery, need switch a battery.
redmi.SwitchBattery();

//hello, I want to produce a Samsung Galaxy Note III, and supports its benchmark.
typedef SmartPhone<SnapDragonS800, FHD, ReplacableBattery, QuadCorePolicy, OLEDDisplayPolicy > SamsungNote3;

SamsungNote3 note3;

note3.BenchMark();
note3.DisplayOLED();
note3.SwitchBattery();
}
不能换电池的手机(iPhone5), 编译器能帮你检查是否有Policy误用了哦.有了这些Policies类,我们就可以很灵活得实现一个类似手机数据库的东东, 虽然这些代码只是玩具.

练习:也可以给不同的产品加上其他有趣的Policies:
template <class CPU, class DISPLAY, class BATTERY> struct ApplePricePolicy
{
double price()
{
return 1.8 * (CPU::price() + DISPLAY::price() + BATTERY::price());
}
};

template <class CPU, class DISPLAY, class BATTERY> struct XiaomiPricePolicy
{
double price()
{
return 1.3 * (CPU::price() + DISPLAY::price() + BATTERY::price());
}
};
玩了这些Policies, 那么我们在具体工程中有什么场景可以利用Policies呢?

1) 在某些设计严格的代码中, 利用Policies可以减少代码,简化设计.比如现有的代码库中有一套接口整齐的Container Policies, 支持类似std::vector, std::list的常用操作. 当你需要用std::set的行为实现这些接口时,可以考虑针对Container写ArrayExtactor, ListExtractor, SetExtractor这样的policies, 避免生成新的container类.

2) 在写一些模板库的时候, 使用policies建模时被证明过有效的技术, 缺点是写这些代码也许不容易,但是使用这些代码的客户程序员一定会很爽.

结束语: Policies是一种模板技术,综合了模板和多继承的优点, 在编写库代码的时候特别有用, 谢谢! 我们下次见.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: