• No results found

Generic Observer with Spring4D

In document Leanpub.more.Coding.in.Delphi.2015 (Page 66-71)

Building an application with the Spring4D’s IObservable<T> is pretty simple. First, we’ll create a new TBaseballGame. This one is a touch simpler:

type

TBaseballGame = class(TObservable<IDisplay>)

private

FGameInfo: TGameInfo;

protected

procedure DoNotify(const observer: IDisplay); override;

public

procedure SetInning(aInningNumber: TInningNumber; aInning: TInning);

procedure SetRuns(aRuns: TRuns);

procedure SetHits(aHits: THits);

procedure SetErrors(aErrors: TErrors);

property GameInfo: TGameInfo read FGameInfo;

end;

TBaseballGamedescends fromTObservable<IDisplay>. That class has an abstract methodDoNotifythat

needs to be overridden.TBaseballGamedoes so and thus provides a way to notify the class’s Observers that

something has changed.

procedure TBaseballGame.DoNotify(const observer: IDisplay);

begin

observer.Update(Self.GameInfo);

end;

We’ll let our observers know that an update has happened in the game in theSetInningmethod:

procedure TBaseballGame.SetInning(aInningNumber: TInningNumber; aInning: TInning);

begin

FGameInfo.Innings[aInningNumber].Top := aInning.Top; FGameInfo.Innings[aInningNumber].Bottom := aInning.Bottom; Notify;

end;

This baseball game class looks a lot like the baseball game from the previous example, except it doesn’t have any of theISubjectmethods. Instead, it descends fromTObservable<T>, which looks like this:

IObservable<T> = interface

procedure Attach(const observer: T);

procedure Detach(const observer: T);

procedure Notify;

end;

TObservable<T> = class(TInterfacedObject, IObservable<T>)

private

fLock: TMREWSync; fObservers: IList<T>;

protected

procedure DoNotify(const observer: T); virtual; abstract;

property Observers: IList<T> read fObservers;

public

constructor Create;

procedure Attach(const observer: T);

procedure Detach(const observer: T);

procedure Notify;

end;

You should see the similarities inIObservable<T>andTObservable<T> and the first demo application.

They both track observers (listeners) and they both have Subjects (TObservable).

We’ll just declare a single observer,TConsoleDisplay, which implementsIDisplay, an interface that we’ll

pass as the parameterized type (as seen above in the declaration of TBaseballGame).TConsoleDisplayis

basically declared the same way as in the first example.

type

IDisplay = interface

['{E118BD99-37BD-461C-AF69-770FD8E18702}']

procedure Update(aGameInfo: TGameInfo);

end;

TConsoleDisplay = class(TInterfacedObject, IDisplay)

procedure Update(aGameInfo: TGameInfo);

end;

The main body of the application is basically the same as the previous example:

var BaseballGame: TBaseballGame; begin BaseballGame := TBaseballGame.Create; BaseballGame.Attach(TConsoleDisplay.Create); BaseballGame.SetRuns(TRuns.Create(1, 0)); BaseballGame.SetHits(THits.Create(2, 0)); BaseballGame.SetInning(1, TInning.Create(0, 1)); ReadLn; BaseballGame.SetRuns(TRuns.Create(1, 4)); BaseballGame.SetHits(THits.Create(2, 5)); BaseballGame.SetErrors(TErrors.Create(0, 1)); BaseballGame.SetInning(4, TInning.Create(4, 0)); ReadLn; end.

Here are a couple of things to note:

• Because it takes advantage of the generic infrastructure provided by the Spring for Delphi framework, the amount of code you have to write to enable the Observer pattern goes way down.

• TBaseballGameis the subject. It takes in listeners, and then notifies them when the score changes.

• TBaseballGameis structured basically like our first demo. The data for runs, hits, and errors can be

updated at any time, and when it is, it simply calls itsNotify method.Notifyiterates over all the

observers and calls theirDoNotifymethod.

Conclusion

So in summation, the Observer pattern ensures that publishing classes (subjects) can communicate updates to their subscribing (observer) classes with very loose and flexible coupling. Observers can be updated and removed at runtime. Adding observers requires no change to subjects and observers can be created and added at anytime. Subjects don’t know much at all about what the observers are up to. It’s all very easy and elegant and loosely coupled – just like you want it.

Introduction

Many of you have traveled, I’m sure,and many of you have no doubt traveled to a country where the standard power outlet is different from the one in your home country. Here in the US, our power outlets consist of two parallel slots and a hole below them. One of the slots is “hot” with electricity, and the hole provides the ground. Countries in Europe have a different power outlet. When I go there, I have to bring a power adapter – a device that has the US output on one side and the European input on the other. I plug the adapter into the European outlet, and then plug my device – usually my computer – into the adapter. My computer’s power adapter can perform the job of converting a range of voltages – usually 110V or 220V – into something that the computer can consume. Depending on where I am in the world, my computer is using up to two adapters to power itself.

A typical power adapter

Sometimes in our code we need an adapter. Sometimes two things we want to work together don’t quite fit and we need to write a class that makes them fit. It’s the job of the “adapter” to make one interface match up with a s second one. This is called the Adapter Pattern. The purpose of the Adapter Pattern is to allow us to wrap up something to make it look and work like something it is not. Often those two things will be similar already, but they need not be. Just like our power adapter, the Adapter Pattern will allow us to take a

TSquarePegand fit it into aTRoundHole.

The Adapter Pattern’s definition is “A pattern that converts one interface into another interface expected by

a different class. The Adapter Pattern allows two disparate systems work together by providing a common interface to objects that otherwise would be incompatible.”

You’d use the Adapter Pattern when you need to use an existing class and its interface doesn’t match the interface that you need to use. An adapter will change an existing class into something expected by the consuming class.

In document Leanpub.more.Coding.in.Delphi.2015 (Page 66-71)