Factory mønstre for menufunktioner

I denne artikel trykker jeg Factory mønster i brug for menufunktioner.

Are You Being Served

I denne artikel vil vi tage en kort pause fra det hårde gennemføre Business Objects til at beskæftige sig med noget, der næsten hver applikation kræver: en menu. Ikke en menu i form af et TMainMenu derivat, men snarere en slags front-ende, som viser en række valgmuligheder og kræver en at blive valgt af brugeren. Et typisk eksempel er en liste over valgmuligheder, for brugeren, når et program begynder: de skal vælge, om at starte i hovedsagen, eller måske rapporteringspakke, generelle serviceafgifter eller admin-systemer. Disse muligheder er typisk præsenteres for brugeren i temmelig rå måder, måske en lille form med et antal knapper, hver med en billedtekst, der beskriver programmet skal køres, eller en liste boks med en række poster. Oftest disse menu-type former er hårdt kodet og uløseligt "kender" hvordan til at lancere den valgte indstilling. Sådanne løsninger deler to ejendomme: de er ufleksible og uinteressant. Denne måned vi bruger Factory mønster (og velovervejet valg af visuel komponent) for at give en bedre løsning.

Kommer til at arbejde

En fabrik mønster bør anvendes, når "en klasse ikke kan forudse klassen af ​​objekter skal skabe". Hovedsagelig en klient objekt bruger egenskaber og metoder til en abstrakt forfader objekt, mens det i virkeligheden er det at manipulere konkrete efterkommere, der er blevet leveret af klassen fabrikken. Nøglen til designet er, at kunden ikke behøver at vide specifikke detaljer om den faktiske objekter det er at manipulere, undtagen de, som de deler deres fælles forfader. Dette effektivt afkobler sådanne klasser påkompileringstidspunktet. Med hensyn til vores behov menuskærm i Delphi, betyder det, at menu ikke behøver at bruge enhederne, der indeholder de formularer, der skal vises, når et bestemt menupunkt er valgt. Denne elegance giver os mulighed for at placere sådanne former inde DLL'er, som er dynamisk indlæst, når brugen vælger en passende menupunkt. Dette har de åbenlyse fordele ved hukommelsesforbrug (kun menupunktet aktivt valgt af brugeren på runtime forbruger hukommelse), og at anvendelsen kan udvides ved blot at levere en ny DLL - den eksisterende menuskærmen kan simpelthen opdage det nærvær og tilføje den til listen præsenteres for brugeren.

I første omgang vil vi overveje det drejer sig om en statisk-bundet sæt menufunktioner; det vil sige, alle tilgængelige operationer udarbejdes i programmet. Bemærk dog, at dette blot er en bekvemmelighed; menuskærmen selv stadig har nogen eksplicit kendskab til de operationer til rådighed, eller hvordan de er forbundet i applikationen. I stedet vil hver menu operation registrere sig med fabrikken og menuen formular afhøre fabrikken for de tilgængelige poster. Den menu er så fri til at præsentere disse poster på nogen måde den finder. Når brugeren vælger et menupunkt, fabrikken anmodes om at konstruere et konkret eksempel på den særlige type, og dette vises derefter.

Fabrikken selv har en meget enkel brugerflade. Det har en metode kaldet register, der bruges af den menuindgange sig til at underrette fabrik af deres eksistens (jeg har brugt Registrere som det efterligner den nomenklatur, der anvendes af andre fabrikker i Delphi selv, såsom registrering af komponenter til visning i komponenten palette). Derudover fabrikken har to ejendomme, der lader en anden klasse observere antallet af medlemmer på fabrikken, og giver adgang til hver. Liste 1 viser en fuldstændig gennemførelse af denne fabrik.

Vi har nu brug for at tilføje nogle menufunktioner til fabrikken. Det er meget simpelt: skabe nogle nye former, tilsæt MenuActionFactory enheden til anvendelser klausul i afsnittet gennemførelsen, og tilføj følgende kode til foden hver formular enhed:

initialisering

MenuActions.Register (TMyNewForm);

Den TMyNewForm henvisning bør erstattes med klassen navnet på den særlige form for den pågældende enhed. Nu hvor vores klasse fabrik er blevet initialiseret, kan vi vende vores sind til, hvordan den kan anvendes.

A la carte-menuer

Som tidligere bemærket, at mange "indlysende" måder at brugeren vælger et valg fra mange er visuelt utiltalende. En lodret liste af brede knapper (med lange billedtekster) er en særlig attraktiv, men overraskende fælles løsning. Overvej, hvordan Windows løser et lignende brugergrænseflade spørgsmål: Control Panel er et godt eksempel på en liste med forskellige operationer. Dette er intet mere end en listevisning, men et sæt af veldesignede ikoner og ensartet layout går en lang vej at give et attraktivt interface. Hertil den indbyggede evne en liste for at omarrangere det ikoner, som det er skaleret, og til at vise ikoner i forskellige størrelser eller en mere beskrivende rapport visning, og du har en næsten ideel markeringsværktøj.

Hvis du vil oprette en ny menu skærm, droppe en listevisning på en tom formular og tilpasse den til klienten området. Jeg kan godt lide at droppe en TCoolBar top-linje på formen og tilføje et par flade knapper (ved hjælp af billeder, rippet fra Explorer) at gøre det muligt for brugeren at ændre ViewStyle af listevisningen. For at gøre listevisningen automatisk arrangere ikonerne, skal du tilføje følgende kode til FormResize begivenhed:

lvwForms.Arrange (arDefault);

Endelig har vi brug for at tilføje et billede liste til formularen og knytte det til listevisningen LargeImages ejendom.

Vi har nu brug for at vise menuen muligheder inden for fabrikken i listevisningen. Dette indebærer cykling gennem de registrerede menupunkter og skabe en ny liste element for hver. Et punkt på listen skal tildeles et billede indeks for deres ikon, og en billedtekst - disse vil blive taget direkte fra de samme navngivne egenskaber fra den registrerede form. Det er muligt at tilføje yderligere oplysninger til menupunkterne for eksempel når listevisningen er i vsReport mode; kan det være en god idé at tilføje en lang beskrivelse vises som en anden søjle, så nybegyndere kan få en bedre forståelse hvad hver handling gør. Et godt eksempel på dette kan ses i Management Console i Windows 2000.

Nøglen til denne proces er, at menuen præsentationsform selv skal bruge kun MenuActionFactory enheden selv, og ingen af ​​de specifikke menupunktet former. Disse specialiseringer er helt skjult på den fabrik; som navnet antyder det vil skabe en konkret klasse af den korrekte type, uden kunden nogensinde behøver at kende den nøjagtige detaljer.

Oversigt 2 viser koden er nødvendige for at udfylde listevisningen. Bemærk, at da det cykler gennem hver registreret menu handling, fabrikken skaber den ønskede klasse. Menuen formular opretter derefter en ny liste post med relevante oplysninger, før at ødelægge objektet. Bemærk, at for at dette kan fungere korrekt konstruktørernes på de registrerede klasser skal være virtuel; dette er allerede tilfældet for visuelle komponenter såsom formularer (fordi Delphi bruger en fabrik til at oprette dem som de er streamet fra .DFM filer og ressourcer), men for brugerdefinerede klasser, vil det være nødvendigt at indføre en virtuel konstruktør i deres klasse hierarki.

Når denne formular præsenteres for brugeren, og et punkt vælges ved at dobbeltklikke, så kan vi igen bede vores fabrik til at skabe den særlige form, der kræves, som derefter kan vises. Der er absolut ingen begrænsninger om, hvad disse former kan gøre, men elegancen af ​​fabrikken mønster er, at kunden af ​​fabrikken kan forblive uvidende om detaljer. Yderligere menupunkter kan tilføjes til en udviklende system, blot ved at oprette en ny formular og registrere dem med fabrikken - de vil straks blive tilføjet i menusystemet, næste gang programmet køres. Denne mangel på kobling mellem elementer af systemet i høj grad letter ansøgning vedligeholdelse for store projekter - blot ved at registrere klasser med en fabrik bygherren ved, at en del af det program, der har en interesse i sådanne ting vil blive opdateret.

Det er let at se, hvordan denne menu løsning er i sig selv generisk og kan anvendes til en række applikationer eller endda den samme anvendelse et antal gange. I mange af mine programmer har jeg en hovedmenu, der viser alle de store moduler i suite, to sådanne moduler bliver rapportering og generelle forsyningsselskaber. Når der er valgt netop disse moduler de til gengæld vise en yderligere menu af de rapporter og hjælpeprogrammer til stede i systemet hhv. Alle disse menu danner dele kode gennem visuel form arv, kun afviger på fabrikken, som de bruger (der er forskellige fabrikker til moduler, rapporter og hjælpeprogrammer). Den visuelle form, forfader deles af alle tre kan være ganske funktionelt, dynamisk tilføje TMenuItem poster til ansøgning for hvert menupunkt. Nøgleordet her er genbrug af kode gennem løs kobling - en stor fordel af objektorienterede principper. Til sammenligning traditionelle fremgangsmåde nøje koble de unikke menu formularer til deres specifikke menu tiltag ser ufleksibel og uhåndterlig.

Sidste artiklens problem

Sidste artikel diskuterede vi, hvordan en delegeret objekt kunne tilvejebringes at vare business objekt information, og spørgsmålet blev stillet, hvor der kan denne bestemmelse finde sted? Det indlysende svar på spørgsmålet er at skabe data management objekt i konstruktøren af ​​virksomheden objekt:

konstruktør TMyPDObject.Create;

begynde

arvet;

// Pass krævede oplysninger til constructor

DMObject: = TMyDMObject.Create;

ende;

Problemet med denne tilgang er, at alle virksomheder objekt vi skaber har overhead af konstruere en data management objekt. Afhængig af din database, kan dette være en dyr operation, og dermed særdeles uønsket, især når det vurderes, at mange virksomheder genstande vil blive bygget og ødelagt, når programmet kører. Ved at undersøge grænsefladen til klassen, kan det ses, at lasten og Gem metoder er statsløse - alt arbejde opnås i den enkelt metode opkald. Derfor forudsat ansøgningen ikke kræver multi-threaded databaseaktivitet i separate dele af problemområdet, kan en enkelt data management objekt deles blandt de mange forekomster af virksomheden objekter det kan håndtere. Endnu bedre, kan oprettes denne ene datastyring objekt på ansøgning opstart (eller første gang det er nødvendigt). Kan oprettes Disse data forvaltning genstande som private globale objekter i afsnittet gennemførelsen af ​​problemområdet, eller, mere elegant, kan en fabrik bruges til at knytte data forvaltning klasser med business objektklasser og oprette dem korrekt.

(((Oversigt 1 - En implementering af en fabrik til menufunktioner)))

Enheden MenuActionFactory;

grænseflade

anvendelser

Klasser, former;

typen

TMenuAction = TForm;

TMenuActionClass = klasse af TMenuAction;

TMenuActionFactory = klasse

private

MenuActionList: tListe;

funktion GetCount: Integer;

funktion GetMenuAction (Index: Integer): TMenuAction;

offentlige

konstruktør Opret;

destructor Ødelæg; tilsidesætte;

procedure Register (MenuActionClass: TMenuActionClass);

ejendom Count: Integer læste GetCount;

ejendom MenuAction [Index: Integer]: TMenuAction læste GetMenuAction; standard;

ende;

Var

MenuActions: TMenuActionFactory;

implementering

konstruktør TMenuActionFactory.Create;

begynde

arvet;

MenuActionList: = TList.Create;

ende;

destructor TMenuActionFactory.Destroy;

begynde

MenuActionList.Free;

arvet;

ende;

fungere TMenuActionFactory.GetCount: Integer;

begynde

Resultat: = MenuActionList.Count;

ende;

fungere TMenuActionFactory.GetMenuAction (Index: Integer): TMenuAction;

begynde

Angivet ((Index> = 0) og (Indeks <Count), »Index uden for rækkevidde. ');

// Konstruere og returnere den valgte post

Resultat:. = TMenuActionClass (MenuActionList [Index]) Opret (nul);

ende;

procedure TMenuActionFactory.Register (MenuActionClass: TMenuActionClass);

begynde

MenuActionList.Add (MenuActionClass);

ende;

initialisering

MenuActions: = TMenuActionFactory.Create;

færdiggørelsen

MenuActions.Free;

ende.

(((End oversigt 1)))

(((Oversigt 2 - Brug en fabrik til at udfylde en listevisning)))

procedure PopulateListView (lvwMenu: TlistView; Factory: TMenuActionFactory);

Var

Index: Integer;

MenuAction: TForm;

begynde

for Index: = 0 til Factory.Count - 1 do begynde

MenuAction: = Factory.MenuAction [Indeks];

med lvwMenu.Items.Add do begynde

Billedtekst: = MenuAction.Caption;

ImageIndex: = lvwMenu.LargeImages.AddIcon (MenuAction.Icon);

SubItems.Add (MenuAction.Hint);

ende;

ende;

ende;

(((End oversigt 2)))

Næste i serie