Design Pattern -- 09. Structural. Compsite. Pattern (Delphi Sample)

Source: Internet
Author: User
Intent
  • Compose objects into tree structures to represent whole-part hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.
  • Recursive composition
  • "Directories contain entries, each of which cocould be a directory ."
  • 1-to-convert "has a" up the "is a" hierarchy
Problem

Application needs to manipulate a hierarchical collection of "primitive" and "composite" objects. processing of a primitive object is handled one way, and processing of a composite object is handled differently. having to query the "type" of each object before attempting to process it is not desirable.

Discussion

Define an abstract base class (Component) that specifies the behavior that needs to be exercised uniformly ready SS all primitive and composite objects. subclass the Primitive and Composite classes off of the Component class. each Composite object "couples" itself only to the abstract type Component as it manages its "children ".

Use this pattern whenever you have "composites that contain components, each of which cocould be a composite ".

Child management methods [e.g.addChild(),removeChild()] Shocould normally be defined in the Composite class. unfortunately, the desire to treat Primitives and Composites uniformly requires that these methods be moved to the abstract Component class. see the "Opinions" section below for a discussion of "safety" versus "transparency" issues.

Structure

Composites that contain Components, each of which cocould be a Composite.

Menus that contain menu items, each of which cocould be a menu.

Row-column GUI layout managers that contain widgets, each of which cocould be a row-column GUI layout manager.

Directories that contain files, each of which cocould be a directory.

Containers that contain Elements, each of which cocould be a Container.

Example

The Composite composes objects into tree structures and lets clients treat individual objects and compositions uniformly. although the example is abstract, arithmetic expressions are Composites. an arithmetic expression consists of an operand, an operator (+-*/), and another operand. the operand can be a number, or another arithmetic expresssion. thus, 2 + 3 and (2 + 3) + (4*6) are both valid expressions.

Check list
  1. Ensure that your problem is about representing "whole-part" hierarchical relationships.
  2. Consider the heuristic, "Containers that contain containees, each of which cocould be a container. "For example," Assemblies that contain components, each of which cocould be an assembly. "Divide your domain concepts into container classes, and containee classes.
  3. Create a "lowest common denominator" interface that makes your containers and containees interchangeable. It shocould specify the behavior that needs to be exercised uniformly internal SS all containee and container objects.
  4. All container and containee classes declare an "is a" relationship to the interface.
  5. All container classes declare a one-to-define "has a" relationship to the interface.
  6. Container classes leverage polymorphism to delegate to their containee objects.
  7. Child management methods [e.g.addChild(),removeChild()] Shocould normally be defined in the Composite class. unfortunately, the desire to treat Leaf and Composite objects uniformly may require that these methods be promoted to the abstract Component class. see the Gang of Four for a discussion of these "safety" versus "transparency" trade-offs.
Rules of thumb
  • Composite and Decorator have similar structure diagrams, reflecting the fact that both rely on recursive composition to organize an open-ended number of objects.
  • Composite can be traversed with Iterator. visitor can apply an operation over a Composite. composite cocould use Chain of Responsibility to let components access global properties through their parent. it cocould also use Decorator to override these properties on parts of the composition. it cocould use Observer to tie one object structure to another and State to let a component change its behavior as its state changes.
  • Composite can let you compose a Mediator out of smaller pieces through recursive composition.
  • Decorator is designed to let you add responsibilities to objects without subclassing. composite's focus is not on embellishment but on representation. these intents are distinct but complementary. consequently, Composite and Decorator are often used in concert.
  • Flyweight is often combined with Composite to implement shared leaf nodes.
Opinions

The whole point of the Composite pattern is that the Composite can be treated atomically, just like a leaf. if you want to provide an Iterator protocol, fine, but I think that is outside the pattern itself. at the heart of this pattern is the ability for a client to perform operations on an object without needing to know that there are using objects inside.

Being able to treat a heterogeneous collection of objects atomically (or transparently) requires that the "child management" interface be defined at the root of the Composite class hierarchy (the abstract Component class ). however, this choice costs you safety, because clients may try to do meaningless things like add and remove objects from leaf objects. on the other hand, if you "design for safety", the child management interface is declared in the Composite class, and you lose transparency because leaves and Composites now have different interfaces.

Smalltalk implementations of the Composite pattern usually do not have the interface for managing the components in the Component interface, but in the Composite interface. C ++ implementations tend to put it in the Component interface. this is an extremely interesting fact, and one that I often ponder. I can offer theories to explain it, but nobody knows for sure why it is true.

My Component classes do not know that Composites exist. they provide no help for navigating Composites, nor any help for altering the contents of a Composite. this is because I wocould like the base class (and all its derivatives) to be reusable in contexts that do not require Composites. when given a base class pointer, if I absolutely need to know whether or not it is a Composite, I will usedynamic_castTo figure this out. In those cases wheredynamic_castIs too expensive, I will use a Visitor.

Common complaint: "if I push the Composite interface down into the Composite class, how am I going to enumerate (I. e. traverse) a complex structure ?" My answer is that when I have behaviors which apply to hierarchies like the one presented in the Composite pattern, I typically use Visitor, so enumeration isn' t a problem-the Visitor knows in each case, exactly what kind of object it's dealing. the Visitor doesn' t need every object to provide an enumeration interface.

Composite doesn' t force you to treat all Components as Composites. it merely tells you to put all operations that you want to treat "uniformly" in the Component class. if add, remove, and similar operations cannot, or must not, be treated uniformly, then do not put them in the Component base class. remember, by the way, that each pattern's structure dimo-doesn't define the pattern; it merely depicts what in our experience is a common realization thereof. just because Composite's structure dimo-shows child management operations in the Component base class doesn't mean all implementations of the pattern must do the same.

Delphi Sample

The idea is to define a Composite Class

  1: AComponentClass = class 
  2:    //...
  3:    procedure DoSomething; virtual; 
  4: end;
  5: 
  6: ACompositeClass = class(aComponentClass) 
  7:     aList: tList;  // List of "aComponentClass" Objects 
  8:     procedure DoSomething; override; 
  9:     //... 
 10: end;
 11: 

Now you can call a single class method aComponentClass.DoSomething; in any case, whether the class is a composite class or not. The method “DoSomething” of the CompositeClass for example calls “DoSomething” for every item of the list.

This allows you to ignore the difference between compositions of objects and individual objects.

A simple example for a composition is a directory - a directory is a composition of files. A simple implementation of a class that copies a file or a complete directory with all subdirectories looks like this:

  1: uses Classes,SysUtils,..; 
  2: 
  3:   TFile = class 
  4:   public 
  5:     fName : string; 
  6:   public 
  7:     constructor Create(Name : string); 
  8:     procedure Copy(DstDir : string); virtual; 
  9: 
 10:     property Name : string read fName; 
 11:   end; 
 12: 
 13:   TDirectory = class(TFile)   
 14:   private 
 15:     FileList : TList; 
 16:   public 
 17:     constructor Create(Name : string); 
 18:     destructor Destroy; 
 19:     procedure Copy(DstDir : string); override; 
 20: 
 21:     property Name; 
 22:   end; 
 23: 
 24: // TFile
 25: 
 26: constructor TFile.Create(Name: string); 
 27: begin 
 28:   fName:=Name; 
 29: end; 
 30: 
 31: procedure TFile.Copy(DstDir: string); 
 32: var SrcFilename,DstFilename : string; 
 33: begin 
 34:   SrcFilename:=fName; 
 35:   DstFilename:=IncludeTrailingPathDelimiter(DstDir)+ 
 36:                ExtractFilename(fName); 
 37:   if FileExists(SrcFilename) then 
 38:     Windows.CopyFile(PChar(SrcFilename),PChar(DstFilename),false); 
 39: end; 
 40: 
 41: // TDirectory
 42: 
 43: procedure TDirectory.Copy(DstDir: string); 
 44: var i : integer; 
 45:     RelPath, Separator : string; 
 46: begin 
 47:   if not DirectoryExists(DstDir) then 
 48:     ForceDirectories(DstDir); 
 49: 
 50:   for i:=0 to FileList.Count-1 do 
 51:    if TFile(FileList[i]) is tDirectory then 
 52:    begin 
 53:      RelPath:=ExtractRelativePath(IncludeTrailingPathDelimiter(Name),  TDirectory(FileList[i]).Name);
 54:      Separator := '\'; //It's a filename separator
 55:      TDirectory(FileList[i]).Copy(DstDir+Separator+RelPath)
 56:    end else 
 57:      TFile(FileList[i]).Copy(DstDir);
 58: end; 
 59: 
 60: constructor TDirectory.Create(Name: string); 
 61: var Root,s : string; 
 62:     sr : tSearchRec; 
 63: begin 
 64:   inherited Create(Name); 
 65: 
 66:   FileList := TList.Create; 
 67: 
 68:   Root:=IncludeTrailingPathDelimiter(Name); 
 69:   s:=Root+'*.*'; 
 70:   if FindFirst(s, faAnyFile , sr) = 0 then 
 71:   begin 
 72:     repeat 
 73:       if (sr.Name = '.') or (sr.Name = '..') then continue; 
 74: 
 75:       if ((sr.Attr and faDirectory) <> 0) then
 76:          FileList.Add(tDirectory.Create(Root+sr.Name)) 
 77:       else
 78:          FileList.Add(tFile.Create(Root+sr.Name)); 
 79:     until FindNext(sr) <> 0; 
 80:     FindClose(sr); 
 81:   end; 
 82: end; 
 83: 
 84: destructor TDirectory.Destroy; 
 85: var i : integer; 
 86: begin 
 87:   for i:=0 to FileList.Count-1 do 
 88:     tFile(FileList[i]).Destroy; 
 89:   FileList.Free; 
 90: end;
 91: 

----------------------------------------------------------------------------

  1: program Structural.Composite.Pattern;
  2: 
  3: {$APPTYPE CONSOLE}
  4: 
  5: uses
  6:   SysUtils,
  7:   Pattern in 'Pattern.pas';
  8: 
  9: var
 10:   dir: TDirectory;
 11: 
 12: begin
 13:   try
 14:     try
 15:       dir := TDirectory.Create('c:\test01');
 16:       dir.Copy('c:\test02');
 17:     finally
 18:       FreeAndNil(dir);
 19:     end;
 20:   except
 21:     on E:Exception do
 22:       Writeln(E.Classname, ': ', E.Message);
 23:   end;
 24: end.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.