R2

每一天都是美好的……

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  16 随笔 :: 0 文章 :: 135 评论 :: 0 引用

公告

2004年7月27日 #

Pattern Tips 之五
作者:温昱

感谢:《设计模式》一书的作者Gamma,Helm,Johnson和Vilssides,译者李英军等

----------------------------------说明----------------------------

Builder,Visitor,Observer,Mediator。它们都是Behavioral Patterns(在此认为Creationall Patterns是特殊的Behavioral Patterns)。

更重要的是,它们都是比较“难”的模式,它们的共性是:模式本身就有多个Role协作──比如Builder/Director,Visitor/Element,Observer/Subject,Mediator/Colleague。

----------------------------------Builder----------------------------

●Tip 1:关键字。Complex,Assemble。

●Tip 2:图。


可以看到,Director只控“宏观过程”,具体工作委托(delegate)Builder去做。

●Tip 3:实现和使用。

“Architect或Achitecture工程师”负责写Director和Builder。而Product和Part的实例化(以及把Part组装的Product),是Builder的首要职责。

“Application工程师”负责写Client,Director和Builder一般是由该Client实例化的。Director将具体调用哪个Builder,由Client告诉Director。

MFC中,App/CCtrlView/CListView/CListCtrl/Column and Item分别是Director/Abstract Builer/Concrete Builer/Product/Part。

●Tip 4:优点。Finer control over the assembling process。

控制的“粒度”比较精细体现在Director和Builder的分离上(想一下Abstract Factory是“construct products in one shot”的),Builder的职责是Create a part and assemble it to product,Director的职责是Control builder''s assembling process。

可以通过Director的不同写法来满足不同的需求:可以是“数据驱动”的,比如战略游戏的Director可以去读地图文件生成地图,文档格式转换程序的Director可以去读“源格式文件”生成“目标格式文件”;可以是“用户驱动”的,比如Word或Photoshop这些编辑软件的Director是个大大的消息循环,根据用户的操作来具体构造文档;当然也可以对assembling process硬编码,如书中附带的例子,

Maze* MazeGame::CreateMaze( Mazebuilder & builder )
{
  builder.BuildMaze();  //create the Product

  builder.BuildRoom(1);  //create 3 Parts of Product and assemble them to the Product
  builder.BuildRoom(2);
  builder.BuildDoor(1, 2);

  return builder.GetMaze();
}
●Tip 5:支持变化。

Director可以通过调用Builder来达到“细粒度”的控制,而且它是通过AbstractBuilder这个非常稳定的Interface建立协作的,Director和ConcreteBuilder之间不存在直接的耦合关系。所以,Director和ConcreteBuilder都相当“自由”。图中黄色的Director是新写的,以支持新的Assemble策略,令“Architect或Achitecture工程师”高兴的是,没有对Builder及其子类做任何改动。

----------------------------------Visitor----------------------------

●Tip 1:关键字。New Operation。

●Tip 2:图。


可以看到,为了达到“将Aalgorithm从Data分离出来”的目的,代价是1.5 个对象:新增Visitor,Element增加Accept()操作。

●Tip 3:实现和使用。

我认为当CWnd::SubclassWindow()或CWnd::SubclassDlgItem()时,CWnd就是window obj的Visitor。

●Tip 4:优点。算法集中,易于维护。Related behavior isn''t spread over the classes defining the object structure; it''s localized in a visitor。

●Tip 5:支持变化。Visitor makes adding new operations easy。图中的黄色Class就是假想后来扩充的。

●Tip 6:局限性。

只能用于 The classes defining the object structure rarely change, but you often want to define new operations over the structure。 因为 Most of these operations will need to treat nodes that represent assignment statements differently from nodes that represent variables or arithmetic expressions。所以 Adding new ConcreteElement classes is hard。所以 If the object structure classes change often, then it''s probably better to define the operations in those classes。

Visitor and Element之间是紧耦合。Breaking encapsulation. Visitor''s approach assumes that the ConcreteElement interface is powerful enough to let visitors do their job. As a result, the pattern often forces you to provide public operations that access an element''s internal state, which may compromise its encapsulation.

----------------------------------Ovserver----------------------------

●Tip 1:关键字。Notify,Dependency。

●Tip 2:图。

可以看到,首先,concreteListener调用concreteBroadcaster.AddListener( this )“订阅”消息;然后某时,concreteBroadcaster调用Broadcast()“发布”消息,已订阅消息的concreteListener的ListenToMessage()被调用执行。

●Tip 3:实现和使用。

When the dependency relationship between Broadcasters and Listeners is particularly complex, an object that maintains these relationships might be required. We call such an object a ChangeManager. ChangeManager is an instance of the Mediator pattern.

MFC中,CView就是CDocument的Observer,CListView就是CListCtrl的Observer。

值得说明的是,经典的Mediator模式中,各Colleague是完全“平等”的,任何一个Colleague的变化都可能引起其它Colleague相应改变;但这里的Colleague是由Broadcaster和Listener充当,它们是“不平等”的──Broadcaster总是发起者,Listener总是响应者。哈哈,以此观点,Facade也是一种Mediator了。

下图中,ChangeManager会有一个broadcaster_listener_map数据结构,它可以很复杂,Broadcaster::AddListener()和ChangeManager::Register()的参数都要相应复杂,比如从“订阅某Broadcaster的消息”变成“订阅某Broadcaster的某类消息”或“订阅所有某类消息”。



Observer pattern appears in Smalltalk Model/View/Controller (MVC)。。。



●Tip 4:支持变化。The Observer pattern lets you vary subjects and observers independently. All a subject knows is that it has a list of observers, each conforming to the simple interface of the abstract Observer class。注意图中和Framework内的对象相关的依赖都是良性依赖,被标成绿色,正是这些良性依赖使得Application中的Changeable性高的对象能够比较自由地变化。

●Tip 5:支持框架。相当于一个消息平台,图中已标出。

----------------------------------Mediator----------------------------

●Tip 1:关键字。Collective Behavior。

●Tip 2:图。



可以看到,多个Colleague变化时,都向Mediator报告;Mediator会根据具体哪个Colleague的obj发生了变化,调用相关Colleague的相关操作。

●Tip 3:实现和使用。Colleague passes a reference to itself as an argument to ColleagueChanged() to let the Mediator identify the Colleague that changed。

MFC中的Dialog是典型的Mediator,其所有control都向它报告消息。

●Tip 4:优点。It centralizes control. That can help clarify how objects interact in a system. The Mediator pattern trades complexity of interaction for complexity in the mediator.Colleagues send and receive requests from a Mediator object. The mediator implements the cooperative behavior by routing requests between the appropriate colleague(s).

●Tip 5:支持变化。It decouples colleagues. A mediator promotes loose coupling between colleagues. You can vary and reuse Colleague and Mediator classes independently。可以改Mediator::ColleagueChanged()的具体实现,以实现不同的“更新”策略;也可以增加Colleague,Mediator只需做少许相应改动。改动在图中用黄色标出。

posted @ 2004-07-27 09:51 R2 阅读(453) 评论(0) 编辑

Pattern Tips 之四
作者:温昱

感谢:《设计模式》一书的作者Gamma,Helm,Johnson和Vilssides,译者李英军等

----------------------------------说明----------------------------

Composite,Decorator。它们都是Structural Patterns,它们的关系如下图所示:


----------------------------------Composite----------------------------

●Tip 1:关键字。Uniform,Tree,Recursive。

●Tip 2:图。


可以看到,图中除了多个“泛化关系”之外,就只剩下一个“聚合关系”了(不是“组成关系”),而恰恰是这个“聚合关系”是至关重要的,它就是著名的“反向包含”(Reverse Contain)。

●Tip 3:实现与使用。

对“Architect或Achitecture工程师”,应注意Composite Class有一个member var存放Children,而且这个member var是个Container而不是个Manager,即单个Child的实例化工作Composite是不管的。

class CompositeEquipment : public Equipment {
public:
  virtual ~CompositeEquipment();
  
  virtual Watt Power();
  virtual Currency NetPrice();
  virtual Currency DiscountPrice();
  
  virtual void Add(Equipment*);
  virtual void Remove(Equipment*);
  virtual Iterator* CreateIterator();
  
protected:
  CompositeEquipment(const char*);
private:
  List <Equipment *> _equipment; /////////// note here, a list of children
};
所有Component的实例化工作将由“Application工程师”负责。

在MFC中,有OS维护的window tree就是最典型的例子。

●Tip 4:优点。Makes the client simple。Clients can treat composite structures and individual objects uniformly,这就是抽象的力量,一个抽象节点Component带来的简单好处。当然,这当中,也不能忘了Recursive算法的贡献,Recursive算法一向以代码简短、逻辑清楚著称。

●Tip 5:支持变化。

Makes it easier to add new kinds of components. Newly defined Composite or Leaf subclasses work automatically with existing structures and client code. Clients don''t have to be changed for new Component classes.

图中的黄色Class是后来扩充的,你可以扩充New Leaf,也可以扩充New Composite。

●Tip 6:局限性。Maximizing the Component interface。

One of the goals of the Composite pattern is to make clients unaware of the specific Leaf or Composite classes they''re using. To attain this goal, the Component class should define as many common operations for Composite and Leaf classes as possible. The Component class usually provides default implementations for these operations, and Leaf and Composite subclasses will override them.

Defining the child management interface at the root of the class hierarchy gives you transparency, because you can treat all components uniformly. It costs you safety, however, because clients may try to do meaningless things like add and remove objects from leaves.

----------------------------------Decorator----------------------------

●Tip 1:关键字。forward,before forwarding,after forwarding。

●Tip 2:图。




●Tip 3:实现和使用。是一个退化的Composite,这一点充份表现在member var由一个Container退化成一个Node Pointer。
class Decorator : public VisualComponent {
public:
  Decorator(VisualComponent*);

  virtual void Draw();
  virtual void Resize();
  // ...
  
private:
  VisualComponent* _component; /////// note here, a point to leaf or decorator
};
在ET++中,CStream。。。

●Tip 4:优点。

More flexibility than static inheritance.Furthermore, providing different Decorator classes for a specific Component class lets you mix and match responsibilities. Decorators also make it easy to add a property twice. For example, to give a TextView a double border, simply attach two BorderDecorators. Inheriting from a Border class twice is error-prone at best.

●Tip 5:局限性。When the Component class is intrinsically heavyweight, using the Decorator pattern is too costly to apply.

----------------------------------Decorator and Composite----------------------------

●Tip 1:Composite : A decorator can be viewed as a degenerate composite with only one component.

●Tip 2:Decorator is often used with Composite.

When decorators and composites are used together, they will usually have a common parent class. So decorators will have to support the Component interface with operations like Add, Remove, and GetChild.

However, a decorator adds additional responsibilities──it isn''t intended for object aggregation.

----------------------------------Builder and Composite----------------------------

●Tip 1:A Composite is what the builder often builds。

很显然,Builder and Composite谁也不是谁的超集。证明:一方面,Composite提供Component的递归包含,而Builder不提供;另一方面,Builder提供create和assemble双重服务, 而Composite只提供assemble服务。

我个人的理解,使用Builder模式的Composite模式,就是提供了create和assemble双重服务,使得“Application工程师”不必担负“实例化Component”的任务。

posted @ 2004-07-27 09:51 R2 阅读(376) 评论(0) 编辑

Pattern Tips 之三
作者:温昱

感谢:《设计模式》一书的作者Gamma,Helm,Johnson和Vilssides,译者李英军等

----------------------------------说明----------------------------

Abstract Factory,Factory Method,Prototype。它们都是Creationall Patterns,它们的关系如下图所示:



----------------------------------Abstract Factory----------------------------

●Tip 1:关键字。Family。

●Tip 2:图。书中的原图如下,



我又画了一张,主要是为我后边讨论本模式“支持变化”做准备,



可以看到,Client用了一个Factory,一个Factory可以“生产”一个Product Family(就是多个Product或A Set of Products),Client使用这一个Product Family。

●Tip 3:实现和使用。

从图中可以看到,ConcreteFactory负责实例化ConcreteProduct,但谁来实例化ConcreteFactory呢?图中Client Class仅对AbstractFactory有关联,但是一个Client Class是不能拥有一个Interface的实例的,而只能拥有它的指针或引用(来支持多态);那么Client Class能new AbstractFactory吗,不仅不能,而且要new ConcreteFactory才对呀。

结论,当“Architect或Achitecture工程师”“实现”Abstract Factory模式时,ConcreteProduct的实例化问题已经考虑在内了,因为这恰恰就是该模式的使命。而当“Application工程师”“使用”Abstract Factory模式时,应负责实例化ConcreteFactory。

在ET++中,Abstract Factory模式实现得非常巧妙。具体实例化哪个ConcreteFactory是“Architect或Achitecture工程师”写在Framework中的一些If语句决定的,这些If语句去读环境变量,解除了“Application工程师”去实例化ConcreteFactory的职责。ET++中的WindowSystem的相关研究,请参考本站(lcspace.nease.net)的Framework栏目。

在MFC中,CDocTemplate也是Abstract Factory模式,并且也非常巧妙地用CRumtimeClass机构解决具体实例化哪个ConcreteFactory的问题。

●Tip 4:支持变化。It makes exchanging product families easy。

不同Product Family一般用于支持类似“多种平台”这样的情形(所以Client一般不同时使用多个Product Family)。想像“Application工程师”欲将Client移植到新平台,由于Client Class对AbstractProduct和AbstractFactory是“良性依赖”(在图中用绿色标出),所以不会引起Client Class的代码变动。如果Client Class还负责“实例化ConcreteProduct”,那么只需改区区2处,比如从

#include "BombedMazeFacory.h"
BombedMazeFacory factory;
game.CreateMaze( factory );
改变成
#include "AnotherMazeFacory.h"  //changed
AnotherMazeFacory factory;  //changed
game.CreateMaze( factory );
增加新的Product Family,对“Architect或Achitecture工程师”来说,只需新写图中黄色Class:新写了一个New ConcreteFactory以及它生产的New Product Family。

在MFC中,CFrame/CDocument/CView就是一个Product Family,我们的确很容易地创建New Product Fameliy:CMyFrame/CMyDoc/CMyView。

●Tip 5:局限性。Supporting new kinds of products is difficult。

简单说,就是将“支持这6个Product”换成“支持那6个Product”容易,换成“支持那7个Product”难。因为AbstractFactory的操作的个数和Product Family中Product的个数相对应,它限制了所有ConcreteFactory的接口,这个接口不应也不易随便改。在图中表现得很充份,Client可以有多个,AbstractProduct可以有多个,Product Family可以有多个,ConcreteFactory可以有多个,唯独AbstractFactory只有一个。

在MFC中,CFrame/CDocument/CView就是一个Product Family,你想在Doc/View arch下创建一个4员组,难。

----------------------------------Factory Method----------------------------

●Tip 1:关键字。Subclass,Virtual,Override。

●Tip 2:图。



●Tip 3:实现和使用。

A factory method define an interface for creating an object,它只要求该Method的责任是Factory:你可以将它实现成纯虚函数/空函数/做相关创建的函数,用于不同情况。但是,在C++中它总是虚函数。

在MFC中,CWnd::Create()就是典型的例子。

●Tip 4:支持变化。

可以看到,2个基类Superclass和AbstractProduct是Framework的组成部份,并且Superclass对AbstractProduct是“对接口编程”,所以是“良性依赖”(在图中用绿色标出),不易引起Superclass的代码变动。

图中黄色的类是New Application新加的,Subclass2对Product2是“恶性依赖”(在图中用红色标出),但又有什么关系呢?因为它们都属于Application,而且Subclass2::FactoryMethod()仅仅创建Product2,涉及的代码很少。

●Tip 5:支持框架。Factory methods pervade toolkits and frameworks。The framework must instantiate classes, but it only knows about abstract classes, which it cannot instantiate。Factory methods eliminate the need to bind application-specific classes into your code。A factory method define an interface for creating an object, but let subclasses CAN (may not) decide which class to instantiate。

----------------------------------Prototype----------------------------

●Tip 1:关键字。Copy,Clone itself。

●Tip 2:图。



●Tip 3:实现和使用。

Wall * BombedWall::Clone() const
{
    return new BombedWall( *this );
}
在MFC中,CWnd::Create()就是典型的例子。

●Tip 4:优点。

仅有 1 个“类层次”,而不是象Factory Method一样平行的 2 个。

------------------Abstract Factory,Factory Method,Prototype,Template Method-----------------

●Tip 1:下面是2种典型的应用情况(其中使用Abstract Factory的情况又分2种实现):



可以看到,Abstract Factory模式常要使用Factroy Method模式或者Prototype模式,Template Method经常调用Factroy Method,



●Tip 2:Abstract Factory模式使用Factroy Method模式之例──使用Abstract Factory的MazeGame。注意MazeFactory已内置缺省实现故不是AbstractMazeFactory;还要注意哪里是“对接口编程”,哪里是“对子类名硬编码”。
/////////////////////////////////////////////////////AbstractFactory
#include "Maze.h"
#include "Room.h"
#include "Wall.h"

class MazeFactory //a superclass as a AbstractFactory, but not containing pure virtual method
{
public:
  MazeFactory(); //con
  virtual Maze* MakeMaze() //a factory method, which must be virtual
    {return new Maze;}
  virtual Room * MakeRoom(int n) //a factory method, which must be virtual
    {return new Room(n);}
  virtrul Wall * MakeWall() //a factory method, which must be virtual
    {return new Wall;}
};
/////////////////////////////////////////////////////Client
#include "BombedMazeFacory.h" //这里是对子类名硬编码(类名和文件名一样)

void MazeGame::XXXXXXXX ( void )
{
  BombedMazeFacory factory;  //实例化ConcreteFactory,这里是对子类名硬编码
  CreateMaze( factory );
}

Maze* MazeGame::CreateMaze (MazeFactory & factory) //BombedMazeFacory.h must
                         //   have included MazeFactory.h, so MazeFactory is available
{
  //
  //these 4 statements call Factory to create some Products
  //这里是对接口编程
  //
  Maze* aMaze = factory.MakeMaze();
  Room* r1 = factory.MakeRoom(1);
  Room* r2 = factory.MakeRoom(2);
  Door* aDoor = factory.MakeDoor(r1, r2);
  
  //
  //all the rest statements use these Products
  //这里是对接口编程
  //
  aMaze->AddRoom(r1); 
  aMaze->AddRoom(r2);
  
  r1->SetSide(North, factory.MakeWall());
  r1->SetSide(East, aDoor);
  r1->SetSide(South, factory.MakeWall());
  r1->SetSide(West, factory.MakeWall());
  
  r2->SetSide(North, factory.MakeWall());
  r2->SetSide(East, factory.MakeWall());
  r2->SetSide(South, factory.MakeWall());
  r2->SetSide(West, aDoor);
  
  return aMaze;
}
/////////////////////////////////////////////////////ConcreteFactory
#include "MazeFactory.h"
#include "BombedMaze.h"

class BombedMazeFactory : public MazeFactory //a subclass as a ConcreteFactory
{
public:
  BombedMazeFactory();
  virtual Maze* MakeMaze() //override a factory method, which must be virtual
    {return new BombedMaze;}
};
●Tip 3:Template Method经常调用Factroy Method之例──使用Template Method的MazeGame。注意与使用Abstract Factory的MazeGame比较。
/////////////////////////////////////////////////////Superclass
class MazeGame 
{
public:
  MazeGame(); //con
  
  Maze* CreateMaze(); //a Template Method
  
  virtual Maze* MakeMaze();//4 factory methods, which must be virtual 
  virtual Room* MakeRoom(int n);
  virtual Wall* MakeWall() const
  virtual Door* MakeDoor(Room* r1, Room* r2);
};
/////////////////////////////////////////////////////Template Method
Maze* MazeGame::CreateMaze () 
{
  //
  //本模板方法调用工厂方法MakeXxx()
  //
  
  Maze* aMaze = MakeMaze();
  
  Room* r1 = MakeRoom(1);
  Room* r2 = MakeRoom(2);
  Door* theDoor = MakeDoor(r1, r2);
  
  aMaze->AddRoom(r1);
  aMaze->AddRoom(r2);
  
  r1->SetSide(North, MakeWall());
  r1->SetSide(East, theDoor);
  r1->SetSide(South, MakeWall());
  r1->SetSide(West, MakeWall());
  
  r2->SetSide(North, MakeWall());
  r2->SetSide(East, MakeWall());
  r2->SetSide(South, MakeWall());
  r2->SetSide(West, theDoor);
  
  return aMaze;
}
/////////////////////////////////////////////////////Subclass
#include "MazeGame.h"
#include "BombedWall.h"
#include "RoomWithABomb.h"

class BombedMazeGame : public MazeGame 
{
public:
  BombedMazeGame(); //con
  
  virtual Wall* MakeWall() //override a factory method, which must be virtual
  { return new BombedWall; }
  
  virtual Room* MakeRoom(int n) //override a factory method, which must be virtual
  { return new RoomWithABomb(n); }
};
posted @ 2004-07-27 09:49 R2 阅读(478) 评论(0) 编辑

Pattern Tips 之二
作者:温昱

感谢:《设计模式》一书的作者Gamma,Helm,Johnson和Vilssides,译者李英军等

----------------------------------说明----------------------------

Template Method,Strategy,State。它们都是Behavioral Patterns,它们的关系如下图所示:


----------------------------------Template Method----------------------------

●Tip 1:关键字。Skeleton。

●Tip 2:图。



●Tip 3:支持变化。Subclass可以只改变算法的特定步骤,而不改变和继续使用算法的Skeleton。图中黄色的Class就是后来写的,而且工作量很小,只需Override相应的Virtual函数。其中的ConcreteClass3的改动量更小,它从已有的ConcreteClass1继承,只Override其中的一个Virtual函数。

Template Method可以说是最常见的模式,在MFC中,全局函数AfxWndProc()就是一例。

●Tip 4:支持框架。著名的Framework方面的“好莱坞法则”(Don''t call us, we''ll call you )就是主要由Template Method支持的“反向控制”(Superclass调用Subclass的Method)产生的。

----------------------------------Strategy----------------------------

●Tip 1:关键字。Aalgorithm Family。

●Tip 2:图。


可以看到,为了达到“将Aalgorithm从Data分离出来”的目的,代价是Context和Strategy 2 个对象。

●Tip 3:实现和使用。

实例化问题。从图中可以看到,Context和ConcreteStrategy的实例化,都将由“Application工程师”负责。

case语句。case语句到了ConcreteStrategy中了吗?“Application工程师”不写case语句了,改“Architecture工程师”要写了。有空研究一下Borland ObjectWindow的源码。

Borland ObjectWindow之Dialog验证用户输入合法性,用了Strategy模式:


●Tip 4:支持变化。Strategy lets the algorithm vary independently from clients that use it。图中的黄色Class就是假想后来扩充的。

●Tip 5:局限性。

Strategy and Context之间是紧耦合。Strategy and Context interact to implement the algorithm. A context may pass all data required by the algorithm to the strategy when the algorithm is called. Alternatively, the context can pass itself as an argument to Strategy operations. That lets the strategy call back on the context as required.

Strategy对Clients不能完全透明。Clients must be aware of different Strategies。 Therefore you should use the Strategy pattern only when the variation in behavior is relevant to clients。想想看,Client要负责ConcreteStrategy(和Context)的实例化,正是决定选哪一个ConcreteStrategy的过程,使得“Strategy对Clients不能完全透明”。

----------------------------------State----------------------------

●Tip 1:关键字。Objects for States。

没什么可说的,就是Strategy用于特殊的目的。

----------------------------------Strategy and Builder----------------------------

同Builder模式对比。同:2个东西要实例化。异:知一知2。

----------------------------------Strategy and Decorator----------------------------

Strategy: A decorator lets you change the skin of an object; a strategy lets you change the guts. These are two alternative ways of changing an object。

posted @ 2004-07-27 09:49 R2 阅读(432) 评论(0) 编辑

Pattern Tips 之一
作者:温昱

感谢:《设计模式》一书的作者Gamma,Helm,Johnson和Vilssides,译者李英军等

----------------------------------说明----------------------------

Adapter,Bridge,Facade,Proxy。它们都是Structural Patterns,它们的关系如下图所示:


----------------------------------Adapter----------------------------

●Tip 1:关键字。existing,reuse。

●Tip 2:图。

可以看到,Adapter是在Target和Adaptee已经existing的情况下,临危受命的,是“事后工程”。

●Tip 3:实现和使用。到底使用Class Adapter还是Object Adapter,要视不同情况而定。

Class Adapter优点:Lets Adapter override some of Adaptee''s behavior, since Adapter is a subclass of Adaptee。另外,不必引入额外的实例化问题,Introduces only one object, and no additional pointer indirection is needed to get to the adaptee。

Class Adapter缺点:Adapts Adaptee to Target by committing to a concrete Adapter class. As a consequence, a class adapter won''t work when we want to adapt a class and all its subclasses.

Object Adapter优点:Lets a single Adapter work with many Adaptees─that is, the Adaptee itself and all of its subclasses (if any). The Adapter can also add functionality to all Adaptees at once.

Object Adapter缺点:Makes it harder to override Adaptee behavior.

MFC本身就是Adapter的例子,Win32 API是基于func的,MFC是基于class的。

●Tip 4:优点。可用于整合遗留系统。

●Tip 5:局限性。如果遗留系统还在发展、变化和增长之中,整合的开销会很大。

----------------------------------Bridge----------------------------

●Tip 1:关键字。Abstraction and its Implementation。

●Tip 2:图。



可以看到,我把图分成了4个Layer:Application,Abstraction,Logic,Implementor。

Application层的Client使用Abstraction层的抽象对象,这些抽象对象是和具体平台无关的;Abstraction层的抽象对象又是由Implementor层的对象实现的,和具体平台有关的对象在Implementor层;为了更加清晰地说明问题,我在Abstraction层和Implementor层之间增加了Logic层,该层是Implementor如何实现Abstraction的程序逻辑。

其实,Bridge模式是非常典型的Layer-like模式。为了对比方便,我放一张ET++的Layer图在下边。

●Tip 3:实现和使用。

在此集中讨论一个问题:RefinedImplementor的实例化问题。因为RefinedAbstraction肯定是由Client实例化的,但RefinedImplementor的实例化却可以分为2种情况:

第1种情况,由Abstraction或RefinedAbstraction实例化。这要求Abstraction或RefinedAbstraction知道所有的RefinedImplementor,具体实例化哪一个,可以通过Abstraction::Abstraction(para)的参数来确定。

第2种情况,委托给别的对象来实例化。典型的,可以委托给一个Abstract Factory来实例化。这样,Abstraction只需要知道Implementor这个Interface,这是一个良性依赖,在图中被我画成了绿色。

ET++中,和平台无关的Window是用和平台相关的WindowPort实现的,但后者的实例化是委托(delegate)WindowSystem这个Abstract Factory来完成的:

class Window {
...
protected:
  WindowImp* GetWindowImp(); ///////////////call Abstract Factory and return Implementor
private:
  WindowImp* _imp;  ////////////save GetWindowImp() ''s return value
...
};

WindowImp* Window::GetWindowImp () {
  if (_imp == 0) {
    _imp = WindowSystemFactory::Instance()->MakeWindowImp();////////////////////WindowSystemFactory is a Singleton
  }
  return _imp;
}

●Tip 4:支持变化。Putting the Window abstraction and its implementation in separate class hierarchies。You can extend the Abstraction and Implementor hierarchies independently。图中的黄色Class就是假想后来扩充的。

----------------------------------Proxy----------------------------

●Tip 1:关键字。Placeholder,Control。

●Tip 2:图。


可以看到,Proxy和Realthing的对外接口是相同的。

●Tip 3:实现和使用。讨论两个关键字:

Placeholder。可以是a direct reference to its real subject,比如在同一台PC上(且在同一个Application内),DrawProxy之间调用Draw;也可以是only an indirect reference,比如跨网络的应用,可能只知道“host ID and local address on host”,哈哈,就是“IP地址+端口号”。

Control。之所以Proxy,就是为了能Control,或者说为了智能:智能保护,智能拒绝,智能回收,智能降低开销。

在COM中,有智能指针SmartPointer。.Net中的SmartClient说不定也是。。。

----------------------------------Facade----------------------------

●Tip 1:关键字。Subsystem,Higher-level Interface。

●Tip 2:图。


可以看到,Facade封装了多个Class。

●Tip 3:实现和使用。

在Facade Class上还可以做些文章,以进一步降低耦合度,比如Facade本身可以派生Subclass,或者用委托(delegate)来配置Facade。The coupling between clients and the subsystem can be reduced even further by making Facade an abstract class with concrete subclasses for different implementations of a subsystem. Then clients can communicate with the subsystem through the interface of the abstract Facade class. This abstract coupling keeps clients from knowing which implementation of a subsystem is used. An alternative to subclassing is to configure a Facade object with different subsystem objects. To customize the facade, simply replace one or more of its subsystem objects.

使用名字空间。A class encapsulates state and operations, while a subsystem encapsulates classes. The C++ standardization committee added name spaces to the language [], which will let you expose just the public subsystem classes.

在ET++中,有个称为browsing tools的Subsystem,其中的ProgrammingEnvironment就是Facade。ET++中的相关研究,请参考本站(lcspace.nease.net)的Framework栏目。

●Tip 4:支持变化。Lets you vary the components of the subsystem without affecting its clients。

----------------------------------Proxy and Decorator----------------------------

●Tip 1:Proxy是Decorator特例。

当一个Decorator模式,Decorator不能递归修饰Decorator,而且也不要before forwarding和after forwarding的操作了,仅仅就是forward,Decorator模式也就退化成Proxy模式了。

----------------------------------Adapter,Bridge,Facade and Proxy----------------------------

Adapter,Bridge,Facade and Proxy这4种模式,其实都可以归为“Layer-like模式”。想想看,它们确实都是“Layer间单向调用服务”的。

当然,从逻辑上来讲,这4个模式又分为2组:

Bridge and Facade──涉及逻辑层次“不同”的2个Layer。

Adapter and Proxy──涉及逻辑层次“相同”的2个Layer。

下面是典型的Layer-like模式的示意图:


posted @ 2004-07-27 09:48 R2 阅读(486) 评论(0) 编辑

Pattern Tips 之○
作者:温昱


感谢:《设计模式》一书的作者Gamma,Helm,Johnson和Vilssides,译者李英军等

----------------------------------前言----------------------------

本文是我阅读《设计模式》时以及工作实践中的一些体会,交流以待斧正。

----------------------------------本文组织----------------------------

对于“关系密切”或“实现相似”的模式,会放在一起讨论。

对于每个模式,典型地,会有“关键字”、“图”、“实现和使用”、“支持变化”、“支持框架”和“局限性”这些方面的Tip。

关于“实现和使用”,为了讨论方便,本文假设有2个Team:Architecture Team和Application Team。其中“实现”在本文中偏重指“Architect或Architecture工程师实现Design Pattern本身”,“使用”在本文中偏重指“Application工程师调用Design Pattern”。

至于为什么以“关键字”、“图”、“实现和使用”、“支持变化”、“支持框架”和“局限性”的顺序来讨论,我是这么考虑的:

----------------------------------模式关系图----------------------------

书中的“设计模式之间的关系”图对指导实践非常有价值,因为实际应用中往往需要多个模式相互配合,

我又重新画了一张,3类模式分别画成不同的颜色,所有依赖都尽量画成向上的箭头,明显违反此规则的依赖画成红色箭头,


posted @ 2004-07-27 09:47 R2 阅读(552) 评论(0) 编辑

谈谈软件项目管理的重要性
作者:马云冬(xacn)

随着中国加入WTO后,外界对中国的软件业带来了机遇和挑战;为新新的软件行业注入新的活力。但细细一想,其实所带来的更多的是挑战。我所说的挑战不单只是个人开发中的水平问题,更多的是我国软件项目管理的问题。下面我就几个方面来谈谈。

一、开发人员问题:

1、你开发中按软件工程做了吗?

软件工程,这对软件开发员来说是多么熟悉的字眼,但其中的内涵你又知道多少? 再进一步说,在开发中按软件工程来做的又有多少? 大家常在网上听到一些程序员在抱怨说:“我加班加点地写了10万行的代码,所以老板把我给开除了。”这话扎听有点好笑,但细细一想这是他的悲哀。他写的代码虽多,可是关键的又有多少。如果里面的代码只要有80%的是关键的,我想老板是决对不会开除他的。问题出在10万行代码中有多少是关键的。先不说写这10万行代码所花的时间了,就说以后的维护问题,要是你到一个公司,老板首先要求你看完这10万行代码。我想你第一想到就是走人,第二想到的还是走人。因为对于一个没有按软件工程来进行开发的程序,不要说是这么多的代码;那怕是几十行、几百行,读起来也是很难受的事。记得我刚到深圳找工作,老板让我接手另一个程序员所开发的一个小系统。这样理所当然的就是看设计文档,可是没有,这样只有看源程序;当我打开源程序时,我呆了。看了一天代码,我决定走人。当我向BOOS提出时,老板给我做思想工作。当然加薪也是少不了的,这也打破加薪的记录了。所以我就留下了。之后我开始看代码。在我看代码的同时真不知道把写这程序的程序员骂了不知道多少次。这让我更加增强了对软件工程的认识。为了不让以后继我的程序员不骂或少骂我。所以得好好按规定《软件工程》办事。说这些只是想让大家静下来想想,你在开发中按软件工程做了吗?

2、你是先写文档再写程序的吗?

一个好的程序是先写好设计文档再进行编程的,在设计文档的指导下,才能写出安全的代码。在文档的指导下,这样写出来的程序至少不会出现写了10万的代码还被老板开除的情况。如果你不写文档,一开始就写程序,这样你就不会按已设计好的路线走,而是想到哪写到哪。小功能还好说,要是大功能,你想想等你写下一个时,回过头来看原来写的,你早就不知所云了,那时你就觉得好像在云里雾里乱走,修改的代码也就更不安全了。我所说写文档不是说很正规的,那怕你只用一张搞纸划上几画也要画点出来(这是对小功能来说的)。对于大的程序来说,你就必须正规的写了。因为这样才能详细的记下你的设计思想,如果开发一段时间后,感觉须要对一些功能进行修改或变态时,记住别删除原来的,而是在下面进行变更说明。这样再次看文档时你就会更清楚为什么要这么做的原因。一看就明白不是多好。总比你去再想以前的设计花的时间要少,如果删除原来的设计思想,当你再次看修改或变动的功能时,你可能会对其不理解。这是多么可怕的事呀!我想作为程序员应该知道文档的重要性。可是在一些小公司,先写文档后写程序的开发员又有多少。要成为一个好的程序员大家想想自己该怎么做的吧!

二、软件项目管理问题:

1、现代的软件开发,技术不是关键:

随着日益增长的软件需求和软件系统功能的增强,过去一个人开发的历史已不复存在。现在单枪匹马写程序也只是一种娱乐。我们一般开发的系统都是一个小组才能完成的。所以管理才是开发出好的软件的前提条件,没有管理一定出不来好的软件,当然有管理也不一定出软件的。一个成功的软件不一定是最好的技术,但在它背后一定有一个好的管理。所以现在的软件开发已不像从前把技术放在第一,而是该把管理放在第一位。我在网上看到一篇关于中国软件和印度软件的比较。我现在记的不是太多,但对我影响最深的是他们会去权衡技术和开发效率问题。如现在开发一个软件,用户要求在三个月内完成,你在做系统分析时也认为在三个月能完成。但你没有考虑到一些细节,你写完系统的总体设计,在进行详细设计时碰到要建一张不是太大的路由表。这时大多数国内的设计人员就会想用什么算法,去花很多时间去设计研究新的算法和技术,而人家首先考虑的是系统的运行环境,而这个软件设计了是在(CPU:1.1G,内存:512M)中运行,用户也没特意提出其运行效率要求。所以人家就在内存中开一个大数组来对这个路由表进行操作。从这点看,人家注重的是软件的整体,而不像国内大多数据设计员那样,把个体放在首位。其实这方面我觉得我们的开发员应当多向***学习(本人不是***员,团员也因没交团费被Cancel掉了)。把软件设计的整体放在首位,而不去花太多的时间在不一定成功的技术上。如果花太多的时间在技术上去,这将对系统的按时完成带来影响。我也不是说不该研究技术,我只是说开发中应当以全局为重。如果要加入新的技术,必须在分析时就预算其所需要的时间,并设置技术风险管理。如果风险太大就应当取消用这项技术,改用其它的已成功的技术代替。风险管理这是近来才提出的软件管理方法。它对我们的软件项目有着很好的控制作用。对于一些中、大型系统,它是一把走进成功之门的钥匙。这里就不谈了,我将在下面进行说明。

2、好的管理才能开发出好的软件(小系统除外):

大家都知道,软件开发中有太多的不可预知性。但这种不可预知是对总体来说的,当软件进行到一点程度时,不可预知的东西就会变成可预知的东西。以往的做法是不去管理它,这样所带来的就是项目的失败。要是有好的管理方法就可以控制这些不可预知的东西,软件项目就会一步步随着你的设计思路起向成功。现在就和大家一起讨论一些常用的软件管理方法。

1)错误管理:

小时候当我做错事的时候,我父亲总是把我叫到他身边,对我说:“没事,只要下次不做相同的错事就行了。”这话也许很多家长都对自己的小孩这么讲过。小时还不觉得,慢慢长大后,会发觉其中深刻的道理。这就是说从错误中吸取经验教训。软件项目开发中的错误也是一样。软件开发是一项复杂的活动。一个典型的软件开发项目可能会给我们提供很多的机会去从错误中吸取经验教训。一般的软件项目也会提供少量的错误给我们学习。学过开车的人都知道,教练老是会这么讲:“我希望你们从我身上学习我和前人的的经验,这些经验你们就不要再去试了。如果要试你也许会赔上钱甚至于生命。”虽然软件项目开发不会赔上生命,但是失败的软件项目是一定会赔钱的。所是在软件开发中少不了要对错误进行管理。在项目的错误管理中我一般是这么做的,现在和大家讨论一下:

a、 列出典型错误:

典型错误中有人员方面的。如:对有问题的员工失控、挫伤积极性、人员素质低、英雄主义、项目后期加入人员、开发人员与客户之间发生摩擦、不现实的预期、缺乏有效的项目支持、缺乏各种角色的齐心协力、政治高于物质、充满想象等…

典型错误中有过程方面的。如:过于乐观的计划、缺乏足够的风险管理、缺乏计划、在压力下放弃计划、在模糊的项目前期浪费时间、前期活动不合要求、缺少管理控制、缺少质量保证措施、鲁莽编码等…

典型错误中有技术方面的。如:过高估计了新技术或方法带来的节省量、项目中间切换工具、缺乏自动的源代码控制手段等…

b、 列出自己的最差实践:

注意典型错误,建立自己的最差实践列表,可以避免在以后的项目中犯同样的错误。

c、 列出项目中的最差实践:

组织机构和其他项目组总结经验,学习他们的错误中得到的经验。和其他组同事交流项目开发中的磨难,学习他们的经验。列出潜在的错误,看到它我们就会尽量避免今后犯同样的错误。

打个适当的比喻,典型错误好比我们学车时教练讲的经验,自己的最差实践就像我们在实际开车当中出的问题,而项目中的最差实践就是我们学车前的笔试的书。

公司在发展的同时,也会积蓄一些各方面经验。列出所有的经验,按其分类。系统分析中的经验提供给系统分析,设计人员中的经验提供给管理人员,技术中的经验提供给开发员。这样我们就会有更多的时间花在新的错误的防范上面。开发出来的系统就会一个比一个好。

2)风险管理:

下面先看一下来自一段网上的文章吧!

“一般认为赌博是在冒险。拉斯维加斯***的设计者将***的最大赔付率定为97%,即你花一天时间,往***里塞进100元,最多只能赢回970元。

但是,如果比起软件开发所冒险,拉斯维加斯的赌博简直就可以称为“安全的冒险”了。软件项目所面临的不断变换的用户需求、糟糕的计划与估算、不可信赖的承包人、欠缺的管理经验、人员问题、伤筋动骨的技术失败、性能欠佳...等等不胜枚举的风险,使大型项目按时完成的概率几乎为0,大型项目被取消的概率和赌博一样成败参半(Jones 1991)。”

所以项目开发中对风险进行控制管理就大大提高了软件开发的成功性。软件风险管理工作就是在风险成为影响软件项目成功的威胁之前,识别、着手处理并消除风险的源头。一般我们可以在几个层次上定位、管理风险。

1) 危机管理---救火模式,就是在风险已经造成麻烦后才着手处理它们。

2) 失败处理---察觉到了风险并迅速做出反应,但只是在风险发生之后。

3) 风险缓解---事先制定好风险发生后的补救措施,但不做任何防范措施。

4) 着力预防---将风险识别与风险防范作为软件项目的一部分加以规划和执行。

5) 消灭根源---识别和消除可能产生风险的根源。

1、2、3项都是被动进行的,亡羊补牢,为时已完。所以我们应当着力于预防风险,更好的是消除风险根源。

风险管理由风险评估和风险控制。而风险评估由风险识别、风险分析和风险优先级组成:

● 风险识别:就是提出一个潜在破坏项目进度的风险列表,就像生成错误列表一样。

● 风险分析:评估每一个风险出现的可能性及其影响,判定风险的级别。

● 风险优先级:按风险影响大小排出一个风险优先级,这个风险列表将作为风险控制的基础。

风险控制由风险管理计划,风险化解和风险监控组成。

● 风险管理计划:制定一个应对每个重要风险的方案,同时就确保每一个单独的风险管理计划之间以及与整体项目计划之间相一致。

● 风险化解:每个重要风险所对应计划的执行。

● 风险监控:就是对解决风险的过程进行监控,风险监控还可以包括识别新的风险并将其反馈到正在进行的风险管理进程中等方面的工作。

现在以我以前做的项目来说明一下我是怎样进行风险管理的。

接到项目对项目进行调研工作,在调研中就要注意到克服错误列表中的错误。调研完成后,写需求说明书初稿(一般根据情况至少给出两个以上的方案),为客户进行讲解,结合客户意见再次进行修改。把修改后的说明书和同事进行讨论,再次进行修改。在此期间写出总体设计的初稿(大的框架)。最后再为客户讲解,再次修改少量的功能。客户确定需求满足后就可进行总体设计了。在生成需求分析的同时,注意列出需求中存在的风险。如:需求改变问题、需求定义欠佳等风险。在进行总体设计时,多和客户交流。因为在总体设计中修改需求比在详细设计中修改要容易比在编码阶段修改就更加容易了。之后生成总体设计说明书。同时在总体设计中也要对一些不定的因素进行风险监控。列出风险列表。根据总体设计说明书就可以开始详细设计了。在详细设计中除了要考虑系统设计外还要考虑一些技术风险问题。把很难预见的问题列到风险列表中。注意,从需求分析到详细设计,随着系统开发的进度, 以前不明的因素将会慢慢显露。同时也会出现新的不明因素。这样就让我们必须在整个设计开发过程中进行风险监控、风险识别、风险分析和风险化解工作。同理,在编码中也同样处理。在开发过程中根据分析不同,把风险按阶段分为需求分析阶段风险、总体设计阶段风险、详细设计阶段风险和编码阶段风险。并交由此阶段的人员进行监控和化解。同时,如果在化解安全区(规定解决问题的时间段中)内无法完成解决,则提交专家组(包括到外请的专家顾问)解决( 我们一般是在周五下午的讨论会上进行)。当然软件开发中所碰到的风险是很多的。但不可能完全同时进行风险监控的。通常是把风险列表中认为最会发生的风险乘损失的大小后的最大数进行严格的监控起来。随着开发进度,风险是在变化的,所以风险列表可能会增加也可能会减少。只要风险管理好了。系统就成功了一大半。

3)人员管理:

不同人员之间经验的不同导致绩效差别是有目共睹的,大家可能对不同开发人员之间生产效率差距达10:1的观点较为熟悉,大家也知道一些明确激励措施所带来的正面影响。所以人员管理在软件项目中也有较重的分量。很清楚,人力因素极大地影响着生产效率,同时任何关注提高生产效率的组织首先必须有一套良好的人员激励、团队合作、员工选择及培训的机制。这样才能充分发挥人员的自身能动性。为公司创造更多的价值。

除了以上几个面的管理外还有其它方面的管理也决定软件项目的成功与否。如:团队合作、团队结构、生产率工具等等。这里就不多说。大家还是抽空多看看书。因为只要你选择了从事计算机工作,你就选择了永不能停止的学习、学习,再学习。否则你就将被淘汰。这是多么残酷但又多么现实的事呀!

三、在项目开发中软件工程VS项目管理:

开发员对软件工程是多么熟悉的呀!为什么会有这么熟悉呢?因为现在的项目要求开发员“按章办事”。否则充其量也只是一部编程机器。上面已讲了软件工程的重要性,这里就不多说。现在打个比喻,如果把软件工程比做音乐家,那项目管理就是音乐指挥家。一个好的音乐家一个人能奏出动听的音乐,但一群好的音乐家在一起不一定能揍出好的交响乐。它还必须有一位好的指挥家。软件开发也是一样的,有好的程序员只是前提条件,要开发出好的软件,还要有一个好的管理。

posted @ 2004-07-27 09:37 R2 阅读(1360) 评论(0) 编辑