2008年5月31日星期六

The Observer Pattern for Delphi
Filed under: Design Patterns — Joanna Carter @ 3:29 pm

Introduction

When we use Delphi to design forms and data modules, every time we place a component on the designer, several things change: the form now shows an appropriate representation of the component, the object inspector changes to show the properties of the component and, if we press Alt-F12, we see the .DFM file now contains information about the component. Whether this behaviour was modelled correctly on the Observer pattern or not, the result is that several ‘interested parties’ got to know about a change in the content of the project that we are working on.

If you read the GoF Design Patterns book, you will find much discussion on the semantics and structure of the Observer pattern. Do we want to have one subject and many observers; do we want to have many subjects and one observer; or do we want many observers to keep track of many subjects? The answer to these questions will depend to a great extent on the application that you find yourself developing. Delphi, for example may be seen as using all of these variants in some part of the IDE.

In the same book, you will see that in order to implement the Observer pattern to implement a Digital Clock observer that relates to a Clock Timer subject, use is made of multiple inheritance. But Delphi does not support multiple inheritance… "No, not that old chestnut again!", I hear you cry, "Surely what we need in Delphi v10 is multiple inheritance?". Well, yes and no.

There are two primary mechanisms for circumventing a lack of multiple inheritance: Composition and Interfaces.
Abstract Concepts

Let us start by looking at the abstract concepts of Observers and Subjects as discussed in Gamma’s book:



IObserver

procedure Update(Subject: IInterface);

ISubject
procedure Attach(Observer: IObserver);
procedure Detach(Observer: IObserver);
procedure Notify;


As you can see the basic idea of an Observer is that it can be told when the Subject has changed; this is achieved when the Subject calls the Observer.Update method and passes itself as the Subject parameter. The ISubject consists of methods for attaching and detaching IObservers as well as a Notify method, which iterates through any Observers that are attached.
Composition

If you are not comfortable with using interfaces, which is the simplest way of implementing the Observer pattern, then you need to use Composition to supply the necessary additional functionality to existing classes. Composition involves the placing of an instance of a class that implements a desired behaviour inside a derivative of the class that needs to be extended.



TObserver = class

public

procedure Update(const Subject: TObject); virtual; abstract;

end;

TSubject = class
private
fController: TObject;
fObservers: TObjectList;
public
constructor Create(const Controller: TObject);
procedure Attach(const Observer: TObserver);
procedure Detach(const Observer: TObserver);
procedure Notify;
end;


We start off by writing an abstract class for the Observer that provides a method called Update that can be overridden, depending on the class that is to be an Observer. The Subject class can take care of managing the list of Observers and the broadcasting of updates to them.



constructor TSubject.Create(const Controller: TObject);

begin

inherited Create;

fController := Controller;

end;

procedure TSubject.Attach(const Observer: TObserver);
begin
if fObservers = nil then
fObservers := TObjectList.Create;
if fObservers.IndexOf(Observer) < 0 then
fObservers.Add(Observer);
end;

procedure TSubject.Detach(const Observer: TObserver);
begin
if fObservers <> nil then
begin
fObservers.Remove(Observer);
if fObservers.Count = 0 then
begin
fObservers.Free;
fObservers := nil;
end;
end;
end;

procedure TSubject.Notify;
var
i: Integer;
begin
if fObservers <> nil then
for i := 0 to Pred(fObservers.Count) do
TObserver(fObservers[i]).Update(fController);
end;


The constructor for the TSubject class takes a TObject as a ‘Controller’ parameter and this object is retained for use as the ‘real’ Subject to be sent to each of the Observers; otherwise all the Observers will see is a TSubject and not the actual subject class.
Watching the Clock

The GoF book uses the example of a digital clock to demonstrate the principles of the Observer pattern and we will use that same example here.

Let’s start by designing a simple class to represent the Clock mechanism:



TClockTimer = class

private

fTimer: TTimer;

fInternalTime: TDateTime;

fSubject: TSubject;

procedure Tick(Sender: TObject);

public

constructor Create;

destructor Destroy; override;

function GetTime: TDateTime;

property Subject: TSubject

read fSubject;

end;


This particular clock uses a TTimer to keep its own time and for the purpose of this example will update itself every second.

The signature of the Tick method is that of a TNotifyEvent in order to simplify the handling of the timer interval. In the Tick Method, I set an internal variable to the current time to avoid any difference in time portrayed between calls to GetTime by the attached Observers.



constructor TClockTimer.Create;

begin

inherited Create;

fTimer := TTimer.Create(nil);

fTimer.Interval := 1000;

fTimer.OnTimer := Tick;

fTimer.Enabled := True;

fSubject := TSubject.Create(self);

end;

destructor TClockTimer.Destroy;
begin
fSubject.Free;
fTimer.Enabled := False;
fTimer.Free;
inherited Destroy;
end;

function TClockTimer.GetTime: TDateTime;
begin
Result := fInternalTime;
end;

procedure TClockTimer.Tick(Sender: TObject);
begin
fInternalTime := Now;
fSubject.Notify;
end;


Notice the inclusion of a private TSubject field that will allow us to notify the list of Observers. The constructor not only creates the instance of TSubject, it also passes itself to the Subject constructor, so that the Subject can have a TClockTimer to pass to the Observers during the Notify method.

Every time the TTimer.OnTimer event fires, the internal time field is updated to the current time and then the Subject’s Notify event is called.
Putting on a Face

Now we have a clock mechanism, we also need a face for our clock; a way of displaying the time provided by the mechanism.



TDigitalClock = class;

TClockObserver = class(TObserver)
private
fDisplay: TDigitalClock;
public
constructor Create(const Display: TDigitalClock);
procedure Update(const Subject: TObject); override;
end;

TDigitalClock = class(TPanel)
private
fObserver: TClockObserver;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property Observer: TClockObserver
read fObserver;
procedure ObserverUpdate(const Subject: TClockTimer);
end;


As we have said before, Delphi does not support multiple inheritance and so, to be able to derive our Digital Clock face from TPanel we also need to use composition to mix a TObserver class with the TDigitalClock class. Note also that we have to ensure that the Update method of TPanel is not suitable for responding to calls to TObserver.Update, therefore we have called our method ObserverUpdate to avoid confusion.

Just as we had to pass in the Clock Timer to the Subject, we also have to pass the Digital Clock to the Observer. The Update method of TClockObserver will call ObserverUpdate in TDigitalClock to allow the Text property of the TPanel to be updated.



constructor TClockObserver.Create(const Display: TDigitalClock);

begin

inherited Create;

fDisplay := Display;

end;

procedure TClockObserver.Update(const Subject: TObject);
begin
if (Subject is TClockTimer) then
fDisplay.ObserverUpdate(TClockTimer(Subject));
end;


The Clock Observer class derives from TObserver and overrides the Update method to check if the Subject being passed is indeed a TClockTimer and then passes that Clock Timer to the ObserverUpdate method of the Digital Clock.



constructor TDigitalClock.Create(AOwner: TComponent);

begin

inherited Create(AOwner);

fObserver := TClockObserver.Create(self);

end;

destructor TDigitalClock.Destroy;
begin
fObserver.Free;
inherited Destroy;
end;

procedure TDigitalClock.ObserverUpdate(const Subject: TClockTimer);
begin
Text := FormatDateTime(’tt’, Subject.GetTime);
end;


All that is left for the display class to do is to respond to the update by setting the Text property to the value provided by the Clock Timer subject.
Observer Interfaces

Instead of using Composition to circumvent the lack of multiple inheritance, we can also use Interfaces in a way that allows us to support the concept of deriving a class that ‘inherits’ the behaviour of more than one type.

When describing multiple inheritance, the example of an amphibious vehicle is often used. But the concept of an amphibious vehicle does not truly represent an object that is truly a car and truly a boat; surely it is, more accurately, a vehicle that can behave like a car or like a boat. What interfaces allow us to do is to design classes that support multiple behaviours. So let us go on to look at how we can simplify the Observer pattern using interfaces.



IObserver = interface

[’{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}’]

procedure Update(Subject: IInterface);

end;

ISubject = interface
[’{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}’]
procedure Attach(Observer: IObserver);
procedure Detach(Observer: IObserver);
procedure Notify;
end;


In the above example using Composition, we had to write an abstract class called TObserver that had just the one method, Update, but if you look at these interface declarations, you actually have the equivalent of an abstract class. Essentially an interface is almost the same as an abstract class with a few more features like reference counting and the ability to be mixed with other interfaces in a class.

As with the non-interface method of implementing the Observer pattern, we can implement the ISubject interface once and for all and aggregate an instance of TSubject into our Subject class to avoid rewriting the same code over and over again.



TSubject = class(TInterfacedObject, ISubject)

private

fController: Pointer;

fObservers: IInterfaceList;

procedure Attach(Observer: IObserver);

procedure Detach(Observer: IObserver);

procedure Notify;

public

constructor Create(const Controller: IInterface);

end;


Gamma uses a template List container class to maintain the list of Observers, but as we are not using C++ we will use a IInterfaceList as this is the correct way to store lists of references to interfaces. So let’s go on to look at the implementation for this base TSubject class:



constructor TSubject.Create(const Controller: IInterface);

begin

inherited Create;

fController := Pointer(Controller);

end;

procedure TSubject.Attach(AObserver: IObserver);
begin
if fObservers = nil then
fObservers := TInterfaceList.Create;
fObservers.Add(AObserver);
Notify;
end;

procedure TSubject.Detach(AObserver: IObserver);
begin
if fObservers <> nil then
begin
fObservers.Remove(AObserver);
if fObservers.Count = 0 then
fObservers := nil;
end;
end;

procedure TSubject.Notify;
var
i: Integer;
begin
if fObservers <> nil then
for i := 0 to Pred(fObservers.Count) do
(fObservers[i]. as IObserver).Update(IInterface (fController));
end;


The constructor takes an IInterface reference to the aggregating object (in our example the Clock Timer) and stores it in a Pointer field; this ‘weak reference’ technique avoids reference-counting problems that could cause a memory leak due to the mutual references between the TSubject and its aggregating class.

The Attach and Detach methods are fairly straightforward, but I will go into a little more detail with the Notify method. This method traverses the list of Observers that are attached to the Subject and calls the Update method for each observer that it finds. The fController field that is the real subject (Clock Timer) has to be cast back to an IInterface in order to be passed to the Update method of the Observer interface.
A Universal Ticker

Here is the interface definition for the ‘mechanism’ of our clock:



IClockTimer = interface

[’{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}’]

function GetTime: TDateTime;

end;


The declaration of the TClockTimer implementing class is slightly different from the non-interface version. Instead of deriving from TObject, it derives from TInterfacedObject in order to give us a ready-made implementation of IInterface and then also implements the Clock Timer and Subject interfaces.



TClockTimer = class(TInterfacedObject, IClockTimer, ISubject)

private

fTimer: TTimer;

fInternalTime: TDateTime;

fSubject: ISubject;

function GetTime: TDateTime;

procedure Tick(Sender: TObject);

property Subject: ISubject

read fSubject

implements ISubject;

public

constructor Create;

end;


The main differences are: all methods (apart from the constructor) and properties are now private because they will only be accessed through the supported Interfaces. The Subject property is declared with the implements directive so that any attempt to reference the ISubject interface will be redirected to the embedded TSubject instance.



destructor TClockTimer.Destroy;

begin

fTimer.Enabled := False;

fTimer.Free;

inherited Destroy;

end;


The only difference in the code required for the interface version of this class is the omission of the call to fSubject. Free; this is not necessary or possible, as it will automatically fall out of scope when the Clock Timer is destroyed and Free is not a method of IInterface.
Widgets and Other Furry Animals

In Gamma’s book, the Digital Clock class is derived from a Widget class and from the abstract Observer class previously discussed. As we are not able to use multiple inheritance, we are going to have to find another way of implementing a Widget that is also an Observer. For a very simple demonstration component that you can put on a form, I decided that my ‘Widget’ would be a TPanel. Here is the class declaration:



TDigitalClock = class(TPanel, IObserver)

private

procedure IObserver.Update = ObserverUpdate;

procedure ObserverUpdate(const Subject: IInterface);

end;

procedure TDigitalClock.ObserverUpdate(const Subject: IInterface);
var
Obj: IClockTimer;
begin
Subject.QueryInterface(IClockTimer, Obj);
if Obj <> nil then
Caption := FormatDateTime(’tt’, Obj.GetTime);
end;


Because TPanel already has an Update method that is unsuitable for our purposes, we have to redirect the IObserver.Update method to another method, which I have called ObserverUpdate.

In ObserverUpdate you will see that a check is made to ensure that the Subject being passed in is really a Clock Timer and then, if it is a valid subject, the visual representation on the ‘Widget’ is updated using the GetTime method of the Subject.

After installing this component into the VCL, the only other code needed to get a demonstration going is to declare a private variable of type IClockTimer on a test form then add a button and the following event handlers:



procedure TForm1.FormCreate(Sender: TObject);

begin

fClockTimer := TClockTimer.Create;

end;

procedure TForm1.Button1Click(Sender: TObject);

begin

(DigitalClock1 as ISubject).Attach(fClockTimer as IObserver);

end;


Any number of Digital Clocks can be placed on the form and attached to the Clock Timer and they will all be notified and kept up to date every second.

2008年5月30日星期五

C#设计模式(3)

http://www.cnblogs.com/zhenyulu/articles/36068.html

三、 依赖倒置原则(DIP)

依赖倒置(Dependence Inversion Principle)原则讲的是:要依赖于抽象,不要依赖于具体。

简单的说,依赖倒置原则要求客户端依赖于抽象耦合。原则表述:

抽象不应当依赖于细节;细节应当依赖于抽象;
要针对接口编程,不针对实现编程。

反面例子:



缺点:耦合太紧密,Light发生变化将影响ToggleSwitch。

解决办法一:
将Light作成Abstract,然后具体类继承自Light。

优点:ToggleSwitch依赖于抽象类Light,具有更高的稳定性,而BulbLight与TubeLight继承自Light,可以根据"开放-封闭"原则进行扩展。只要Light不发生变化,BulbLight与TubeLight的变化就不会波及ToggleSwitch。

缺点:如果用ToggleSwitch控制一台电视就很困难了。总不能让TV继承自Light吧。

解决方法二:

优点:更为通用、更为稳定。

结论:
使用传统过程化程序设计所创建的依赖关系,策略依赖于细节,这是糟糕的,因为策略受到细节改变的影响。依赖倒置原则使细节和策略都依赖于抽象,抽象的稳定性决定了系统的稳定性。

四、 接口隔离原则(ISP)

接口隔离原则(Interface Segregation Principle)讲的是:使用多个专门的接口比使用单一的总接口总要好。换而言之,从一个客户类的角度来讲:一个类对另外一个类的依赖性应当是建立在最小接口上的。

过于臃肿的接口是对接口的污染。不应该强迫客户依赖于它们不用的方法。

My object-oriented umbrella(摘自Design Patterns Explained)

Let me tell you about my great umbrella. It is large enough to get into! In fact, three or four other people can get in it with me. While we are in it, staying out of the rain, I can move it from one place to another. It has a stereo system to keep me entertained while I stay dry. Amazingly enough, it can also condition the air to make it warmer or colder. It is one cool umbrella.

My umbrella is convenient. It sits there waiting for me. It has wheels on it so that I do not have to carry it around. I don't even have to push it because it can propel itself. Sometimes, I will open the top of my umbrella to let in the sun. (Why I am using my umbrella when it is sunny outside is beyond me!)

In Seattle, there are hundreds of thousands of these umbrellas in all kinds of colors. Most people call them cars.

实现方法:
1、 使用委托分离接口
2、 使用多重继承分离接口

五、 合成/聚合复用原则(CARP)

合成/聚合复用原则(Composite/Aggregate Reuse Principle或CARP)经常又叫做合成复用原则(Composite Reuse Principle或CRP),就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新对象通过向这些对象的委派达到复用已有功能的目的。

简而言之,要尽量使用合成/聚合,尽量不要使用继承。

o Design to interfaces.
o Favor composition over inheritance.
o Find what varies and encapsulate it.
(摘自:Design Patterns Explained)

区分"Has-A"与"Is-A"

"Is-A"是严格的分类学意义上定义,意思是一个类是另一个类的"一种"。而"Has-A"则不同,它表示某一个角色具有某一项责任。

导致错误的使用继承而不是合成/聚合的一个常见的原因是错误的把"Has-A"当作"Is-A"。

例如:

实际上,雇员、经理、学生描述的是一种角色,比如一个人是"经理"必然是"雇员",另外一个人可能是"学生雇员",在上面的设计中,一个人无法同时拥有多个角色,是"雇员"就不能再是"学生"了,这显然是不合理的。

错误源于把"角色"的等级结构与"人"的等级结构混淆起来,误把"Has-A"当作"Is-A"。解决办法:

六、 迪米特法则(LoD)

迪米特法则(Law of Demeter或简写LoD)又叫最少知识原则(Least Knowledge Principle或简写为LKP),也就是说,一个对象应当对其它对象有尽可能少的了解。

其它表述:
只与你直接的朋友们通信
不要跟"陌生人"说话
每一个软件单位对其它的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。

迪米特法则与设计模式
Facade模式、Mediator模式

使民无知
《老子》第三章曰:"是以圣人之治,虚其心,实其腹,弱其志,常使民无知无欲。"使被"统治"的对象"愚昧"化,处于"无知"的状态,可以使"统治"的成本降低。
所谓"最少知识"原则,实际上便是老子的"使民无知"的统治之术。

不相往来
《老子》云:"小国寡民……邻国相望,鸡犬之声相闻,民至老死,不相往来。"将被统治的对象隔离开来,使它们没有直接的通信,可以达到分化瓦解,继而分而治之的效果。迪米特法则与老子的"小国寡民"的统治之术不谋而合。



参考文献:
阎宏,《Java与模式》,电子工业出版社
[美]James W. Cooper,《C#设计模式》,电子工业出版社
[美]Alan Shalloway James R. Trott,《Design Patterns Explained》,中国电力出版社
[美]Robert C. Martin,《敏捷软件开发-原则、模式与实践》,清华大学出版社
[美]Don Box, Chris Sells,《.NET本质论 第1卷:公共语言运行库》,中国电力出版社
http://www.dofactory.com/Patterns/Patterns.aspx

C#设计模式(2)

http://www.cnblogs.com/zhenyulu/articles/36061.html

《人月神话》焦油坑、没有银弹

* 软件腐化的原因:

问题所在 设计目标
----------------------------------------------------------------------------
过于僵硬 可扩展性(新性能可以很容易加入系统)
过于脆弱 灵活性(修改不会波及其它)
复用率低
粘度过高 可插入性(新功能容易加入系统(气囊加入方向盘))

* 提高系统可复用性的几点原则:
传统复用:
1. 代码的粘帖复用
2. 算法的复用
3. 数据结构的复用

* 可维护性与可复用性并不完全一致

* 对可维护性的支持:


一、 "开放-封闭"原则(OCP)

Open-Closed Principle原则讲的是:一个软件实体应当对扩展开放,对修改关闭。

优点:
通过扩展已有软件系统,可以提供新的行为,以满足对软件的新的需求,使变化中的软件有一定的适应性和灵活性。
已有软件模块,特别是最重要的抽象层模块不能再修改,这使变化中的软件系统有一定的稳定性和延续性。

例子:玉帝招安美猴王
当年大闹天宫便是美猴王对玉帝的新挑战。美猴王说:"'皇帝轮流做,明年到我家。'只教他搬出去,将天宫让于我!"对于这项挑战,太白金星给玉皇大帝提出的建议是:"降一道招安圣旨,宣上界来…,一则不劳师动众,二则收仙有道也。"

换而言之,不劳师动众、不破坏天规便是"闭",收仙有道便是"开"。招安之道便是玉帝天庭的"开放-封闭"原则。

招安之法的关键便是不允许更改现有的天庭秩序,但允许将妖猴纳入现有秩序中,从而扩展了这一秩序。用面向对象的语言来讲,不允许更改的是系统的抽象层,而允许更改的是系统的实现层。


二、 里氏代换原则(LSP)

Liskov Substitution Principle(里氏代换原则):子类型(subtype)必须能够替换它们的基类型。

白马、黑马

反过来的代换不成立
《墨子·小取》说:"娣,美人也,爱娣,非爱美人也……"娣便是妹妹,哥哥喜爱妹妹,是因为两人是兄妹关系,而不是因为妹妹是个美人。因此,喜爱妹妹不等同于喜爱美人。用面向对象语言描述,美人是基类,妹妹是美人的子类。哥哥作为一个有"喜爱()"方法,接受妹妹作为参数。那么,这个"喜爱()"方法一般不能接受美人的实例。

一个违反LSP的简单例子(长方形和正方形)

public class Rectangle
{
private long width;
private long height;

public void setWidth(long width)
{
this.width = width;
}

public long getWidth()
{
return this.width;
}

public void setHeight(long height)
{
this.height = height;
}

public long getHeight()
{
return this.height;
}

}


public class Square
{
private long side;

public void setSide(long side)
{
this.side = side;
}


public long getSide()
{
return side;
}

}


正方形不可以做长方形的子类

using System;

public class Rectangle
{
private long width;
private long height;

public void setWidth(long width)
{
this.width = width;
}

public long getWidth()
{
return this.width;
}

public void setHeight(long height)
{
this.height = height;
}

public long getHeight()
{
return this.height;
}

}


public class Square : Rectangle
{
private long side;

public void setWidth(long width)
{
setSide(width);
}


public long getWidth()
{
return getSide();
}


public void setHeight(long height)
{
setSide(height);
}


public long getHeight()
{
return getSide();
}


public long getSide()
{
return side;
}


public void setSide(long side)
{
this.side = side;
}

}


public class SmartTest
{
public void resize(Rectangle r)
{
while (r.getHeight() >= r.getWidth() )
{
r.setWidth(r.getWidth()
+ 1);
}

}

}


在执行SmartTest的resize方法时,如果传入的是长方形对象,当高度大于宽度时,会自动增加宽度直到超出高度。但是如果传入的是正方形对象,则会陷入死循环。

代码重构

public interface Quadrangle
{
public long getWidth();
public long getHeight();
}


public class Rectangle : Quadrangle
{
private long width;
private long height;

public void setWidth(long width)
{
this.width = width;
}

public long getWidth()
{
return this.width;
}

public void setHeight(long height)
{
this.height = height;
}

public long getHeight()
{
return this.height;
}

}


public class Square : Quadrangle
{
private long side;

public void setSide(long side)
{
this.side = side;
}


public long getSide()
{
return side;
}


public long getWidth()
{
return getSide();
}


public long getHeight()
{
return getSide();
}

}




参考文献:

阎宏,《Java与模式》,电子工业出版社

[]James W. Cooper,《C#设计模式》,电子工业出版社

[]Alan Shalloway James R. Trott,《Design Patterns Explained》,中国电力出版社

[]Robert C. Martin,《敏捷软件开发-原则、模式与实践》,清华大学出版社

[]Don Box, Chris Sells,《.NET本质论 1卷:公共语言运行库》,中国电力出版社
http://www.dofactory.com/Patterns/Patterns.aspx

指定目录的图片2值化

```python # -*- coding: utf-8 -*- """指定目录的图片,自适应2值化 """ import os from PIL import Image import numpy as np imp...