|
为了避免写出一个显示图形和播放声音的引擎,我建议大家看看
实际的例子在:
http://www.gameres.com/bbs/showthread.asp?threadid=5877
面向方面(面向外观)的编程,这是一种更加先进的思想,他有助于软件生产的自动化。
实际上他是建立在原来的OO思想之上的一种新的想法,我们知道,所有的软件开发方法的目的都是为了更有效和简单的管理软件固有的复杂性(因为人本身的记忆和理解能力是有限的),下面我来简单介绍一下历史上几种主要开发方法。
面向过程:由于数据和操作数据的方法被分开了,造成大量必须遵守的规则,这也几以为着人所需要承受的负担也就就非常大,所以这种做法会导致复用,维护,设计的困难。
面向对象:数据被隐藏在对象后面,对象提供自己的接口供外界使用,这种做法把面向过程中需要维护的“数据和操作数据的方法之间的规则”去掉了,更好的是他使用一序列的抽象接口把更多的规则模糊到接口背后。
泛型:这其实也是面向对象的一种高层的抽象,原则上说,他本来只是一种方便的手段,但是由于人们不理解,把所有使用C++模板技术的方法都称为泛型,所以泛型的意义也就变成“和大量型别打交道”的开发方法。
接下来就是面向方面的开发方法,本质上他和面向对象不是一个层上的东西,他位于面向对象之下的层上,他是如何更好的,简单的实现面向对象的方法。所以你会发现我们一切都还是老样子,对象还是对象,接口还是接口,过程也还是过程,只是生成对象的方法变了。
这个重要的不同就是:我们在观察并捕获对象的途径上是不一样的,在面向对象的层次上,最一般的方法就是模拟现实世界来设计对象,如:人,狗,飞机。当然还有很多其他方法,但是不变的是:对象是最低级别的东西。而面向方法并不把捕获对象作为自己的最低层次的任务,他把捕捉方面作为自己的最低层次的任务,如:使用工具,看家,飞行。
也就是说,我们不再把世界的基础组成看作是对象,而是把方面看作是世界的组成基础,对象由方面组合而成,也就是说每个对象可以被分为若干个方面。你可能会反驳说:“把对象分为若干个方面不就是把对现分为若干的更小的对象嘛,只是你叫他们为方面而已嘛”。说这种话的人没有真正理解什么是面向对象,因为方面并不是对象,比如:“飞行”是一种对象吗?他或许只能够算一种行为,在面向方面的方法中我们称他为一个方面,这个方面是一种行为,这种行为就是“飞行”。我们知道,鸟会飞行,飞机会飞行,甚至塑料袋也能飞行,虽然每个飞行原理和表现可能完全不一样,但是我们却能把他抽象到一个接口下,这个接口就是一个方面。有的人可能又要发言了:“还不是用接口嘛,什么方面,还是面向对象嘛!”,哼哼,这可不是哦,首先接口并不是你想象的抽象基类。你可能已经早就厌倦了左手继承抽象基类(接口),右手继承某个实现类,然后再实现大量的配接码,这其实又引入了大量的规则,后果不堪设想。实际上在面向对象中,复用是很困难的,因为对象是变化的,每个人的对在同一设计目的约束下,面对同样的素材,几乎都会得到不同的对象,这实际上造成大量不同粒度的对象,使得代码大量重复,而实际上在A程序员的X类的代码在B程序员里的Y类里完全一样。而面向对象有一个“Sounds Good, But evil”的解法----继承,继承非常可怕,特别是层次深的继承就更是邪恶,他造成大量的规则,甚至比面向过程还要多。我们并不排斥继承,我们只是不赞成深度继承,继承功能全面的类,因为这造成大量的规则。我们在面向方面里也大量使用继承,但是这些继承都是一个层级深度的,而且每个继承的父类都只负责一项工作,我们的类实际上只是一个容器而已。而且我们的父类几乎都不一个“对象的类”,而是一个方面,可以说面向方面产生对象,而面向对象使用对象。这样,我们的复用等级降级到了方面,这样上面的情况就会变为:A程序员的X类里有一个方面P,B程序员的Y类里有一个方面P,如果P是经过测试的话,那么这种复用简直就是完美啊!
现在我们来看看我们如何实作方面,并看看如何把方面组合成对象。
template<class T>
class New_Creator //创建器方面的实现1
{
public:
T* Create() { return new T; }
};
template<class T>
class Clone_Creator //创建器方面的实现2
{
private:
T* obj_;
public:
Clone_Creator() : obj_(NULL) { }
void SetObj(T* p) { assert(p); obj_ = p; }
T* Create() { assert(p); return obj_->clone(); }
};
template<class T, template<class> class creator>
class ObjCreator : public creator<T> { }; //创建器对象
class MyClassCreator;
typedef ObjCreator<MyClass, Clone_Creator> MyClassCreator; //使用方法
简直太美妙了,我们并不满足,我们希望把面向方面的思想更加有用,我么把他扩展为:对于一切事物,我们都能抽象出他们相同的方面,这些方面将成为一切的基础,一切事物都是由他们产生的。
这样的话,我们就可以把这种想法扩展到各种设计的范畴而不仅限于对象的分析。我们计划几乎形成一种经验,所有有共同点的东西我们都把他们抽象出来进行复用。这样就又演化出一种朴实的想法:产生式编程。
产生式编程,本质上就是要达到“更多的复用,更少的代码”。
这样,我们被鼓励起来找到一切可以复用的东西,下面我们就来寻找这些东西。
我先声明一点,就是产生式编程的目的在于代码的复用,他并没有包括设计的方法,设计的方法是没有任何方法可以学习的,只能进行沉淀得到。而面向方面的方法也只是教我们如何看待世界,最多也只是对类的设计,他们都没有对一个软件的架构进行思考,也不能得到任何优秀的设计,因为他们本来就不是“干这一行的”。
我们来看看设计一个软件的过程中所遇到的一序列的问题和流程。我们设计软件,首先是对软件进行分析,得到一个基本的框架,这个过程是可以复用的,我们可以创建一个框架来对这一过程进行复用,在此之后我们就需要填充我们的框架,这个过程就涉及到对象的发现和对象的粒度的确定,这个时候我们可以使用面向方面的思想对对象进行拆分,得到一序列的方面,利用方面进行服用。在组织对象之间的关系的时候我们会使用一些设计模式,这个时候我们可以用接口对这些关系进行抽象。
那么我们如何实现这些想法呢?我们究竟该如何在实际编程中实现他们呢?我们知道,一个好的设计一定表现出大量约束,而约束大多由强类型来实现,一个好的约束最好是在编译期被贯彻。现在我们来看看我们手上的工具,我们手上的工具有“模板”“虚函数”这两个威力无比的工具。他们两个是互相对立的,也是互相补充的,模板强调使用者必须知道你要使用的是什么,而虚函数强调使用者不需要知道你在使用的是什么,模板有意表现出型别,而虚函数则有意把型别模糊。由此我们得到一条结论:1。对于可以确知的型别我们尽量使用模板技术,而对于运行期动态的,二进制的技术我们使用虚函数。但是这并不是真理,因为任何技术都有一个使用范围,如果在一个大量的同种类的型别上使用模板,那么我们就会反而把事情弄得更复杂。所以这里还有一条准则,那就是:2。在需要被传递的对象的类上进行抽象请使用虚函数,在类以及类之间的关系的抽象上使用模板。
下面我们看一个C++惯用法--单件的实现
template<class T>
class Simple_Init //创建方面实现1
{
private:
static T _obj;
public:
static T& Instance() { return _obj; }
};
template<class T>
T Simple_Init<T>::_obj;
template<class T>
class Lazy_Init //创建方面实现2
{
public:
static T& Instance() { static T _obj; return _obj; }
};
template< class T, template<class> class S=Simple_Init>
class Singleton : public S<T>, public T //单件惯用法
{
private:
Simple_Singleton() { }
Simple_Singleton(const Simple_Singleton&);
Simple_Singleton& operator = (const Simple_Singleton&);
operator &() const;
operator T() const;
};
class A_Impl { };
typedef Singleton<A_Impl> A; //单件
|
|