Composite pattern)

Source: Internet
Author: User

1. What is a combination mode?

The combination mode provides a hierarchical structure that allows us to ignore the differences between objects and object sets.

The caller does not know whether the caller is an object or a group of objects, but it does not matter. In the combination mode, the caller does not need to know these

Ii. Example

Suppose we want to describe the file system. There are files and folders in the file system, and folders and files in the folder...

Yes, this is a hierarchical structure. Just like a menu, the menu contains menu items and sub-menus ..

A hierarchy is also a tree structure. We can easily think of defining a node class, including a group of pointers to children, to construct a complete tree.

Then our class diagram will be like this:

Note: file only supports the operations listed in the class diagram. The folder class supports all inherited operations.

The basic design of the class is as follows. This class structure can be used to describe the file system. The following code is used for implementation:

Define the directory base class:

Package compositepattern; import Java. util. arraylist;/*** defines the Directory class * @ author ayqy */public abstract class directory {string name; string description; arraylist <directory> files; /*** Add the specified file/folder to the directory * @ Param dir the file/folder to be added * @ return add successful/failed */Public Boolean add (directory DIR) {Throw new unsupportedoperationexception (); // operation exception thrown by default}/*** Delete the specified file/folder in the directory * @ Param dir file/folder to be deleted * @ return Delete successful/failed */Public boolean remove (directory DIR) {Throw new unsupportedoperationexception (); // operation exception thrown by default}/*** clear all files and folders in the directory * @ return clear success/failure */Public Boolean clear () {Throw new unsupportedoperationexception (); // operation exception thrown by default} public arraylist <directory> getfiles () {Throw new unsupportedoperationexception (); // operation exception thrown by default}/*** Print Output */public abstract void print (); Public String getname () {return name;} Public String getdescription () {return description;} Public String tostring () {return name + description ;}}

P.s. note that the processing method (throwing an exception) specific to the folder in the base class can also be done in a more harmonious way. Each has its own advantages and disadvantages, the reason for throwing an exception is described in detail later.

Note: We define an abstract print method in the base class and want to use the print method to output the entire file tree, the combination mode allows us to easily and elegantly implement this troublesome process.

The file class is implemented as follows:

Package compositepattern;/*** implementation file class * @ author ayqy */public class file extends directory {public file (string name, string DESC) {This. name = Name; this. description = DESC;} @ overridepublic void print () {system. out. print (this. tostring (); // output file information }}

The file class is very simple. Because all operations not supported by file in the base class are implemented by default (throwing an exception), the file becomes quite slim.

The following is the folder class:

Package compositepattern; import Java. util. arraylist;/*** implement the folder class * @ author ayqy */public class folder extends directory {public folder (string name, string DESC) {This. name = Name; this. description = DESC; this. files = new arraylist <directory> () ;}@ overridepublic void print () {// print the Folder Information System. out. print (this. tostring () + "("); // print all files and sub-files in the directory for (directory dir: Files) {dir. print (); system. out. print (",") ;}// print the end mark of folder traversal: system. out. print (")") ;}@ overridepublic Boolean add (directory DIR) {If (files. add (DIR) return true; elsereturn false;} @ overridepublic Boolean remove (directory DIR) {If (files. remove (DIR) return true; elsereturn false;} @ overridepublic Boolean clear () {files. clear (); Return true ;}@ overridepublic arraylist <directory> getfiles () {return files ;}}

The folder class provides its own implementation for all supported operations, and makes some articles in the print method, A very simple loop is used to print and output all the child nodes of the current node (what is this easy to think? That's right, it's a Paster pattern), it seems incredible, but this is one of the benefits of using a combination pattern (providing a natural soil for recursion)

Iii. Example

The class required to describe the file system is implemented above. You can test it to see the effect:

The test code is as follows:

Package compositepattern;/*** implement a test class * @ author ayqy */public class test {public static void main (string [] ARGs) {/* construct the file tree * // * ca.txtb.txtsystemsys.datwindowswin32settingslog.txt win32.config */directory dir = new folder ("C", ""); Dir. add (new file ("a.txt", ""); Dir. add (new file ("B .txt", ""); directory subdir = new folder ("system", ""); subdir. add (new file ("sys. dat "," "); Dir. add (subdir); directory subdir2 = new folder ("Windows", ""); directory subdir3 = new folder ("Win32 ",""); subdir3.add (new folder ("Settings", ""); subdir3.add (new file ("log.txt", ""); subdir2.add (subdir3 ); subdir2.add (new file ("win32.config", ""); Dir. add (subdir2); Dir. print (); // print the output file tree }}

The running result is as follows:

C(a.txt, b.txt, system(sys.dat, ), windows(win32(settings(), log.txt, ), win32.config, ), )

The results are basically the same as we expected, but the disadvantage is that there are redundant comma separators.

To eliminate unnecessary commas, we need to show that the loop does not output a comma when taking the last line, and output a comma for the rest of the time.

It is easy to think of using an explicit iterator (hasnext is not exactly used to determine whether it is the last one? Do not forget that arraylist supports iterators). Let's modify the print method:

Public void print () {// print the Folder Information System. out. print (this. tostring () + "("); // print all files and sub-files in the directory iterator <directory> iter = getfiles (). iterator (); While (ITER. hasnext () {directory dir = ITER. next (); Dir. print (); If (ITER. hasnext () {system. out. print (",") ;}// print the end mark of folder traversal: system. out. print (")");}

The blind extra comma is successfully eliminated.

4. More changes

For example, the output file information of notepad.exe is related to the program.

Now, add a new linkedexe attribute to the file to indicate the executable program associated with the file, the folder does not support this attribute (Here we stipulate that the folder does not support the linkedexe attribute, regardless of whether the program associated with the folder is the resource manager or something else)

To meet new requirements, we have to make some changes. To achieve type consistency, we must add the linkedexe attribute to the base class directory (this may be criticized, but sometimes we have to sacrifice some benefits in exchange for some other benefits ..)

The content in the rectangle is the new things we add. These things are supported by file, but not by folder.

After this change, we can print out all the file information related to notepad.exe. Of course, you also need to modify the print method of the folder class:

Public void print () {// print the file for (directory dir: Files) {try {If ("notepad.exe ". inclusignorecase (dir. getlinkedexe () {dir. print () ;}} catch (unsupportedoperationexception e) {// eats an exception and continues traversal (Folder does not support the getlinkedexe operation )}}}

Have you found anything? It seems that the disadvantages of the combination mode are becoming more and more obvious.

In combination mode, you must ignore the differences between an object and a group of objects and treat them equally.

Yes, we have indeed achieved transparency (in the print method, we do not know whether a file or a folder is being processed)

However, we even abuse the abnormal processing mechanism to cover up the differences between object sets and a single object, so as to pursue "same treatment"

In the end, the value is not worth it. It depends on the actual situation. (We are always sacrificing something in exchange for something more useful. As to whether this sacrifice is worth it, we must weigh it out)

V. iterator and Combination Mode

What about a good iterator? Why didn't I see it? Where is it?

The iterator is hidden in the combination mode. Isn't the print method always using the iterator? (Either the implicit iterator or the display iterator ..)

The iterator used in the above example is called an internal iterator. That is to say, the iterator is hidden in the composition class of the combination mode, so it is not easy to find

Of course, if you like, you can also construct an external iterator, like this:

In directoryiterator, We need to manually maintain a stack structure to record the current position (the internal iterator is supported by the system stack) to implement the hasnext and next methods.

In fact, there is still a problem. The file class obviously does not support the iterator method, but it has inherited from the parent class. What should we do?

  • If the return value is null, the caller must use the if statement for determination.
  • If an exception is thrown, the caller must handle the exception.
  • (Recommended Practice) return an empty iterator (nulliterator). How can I implement the empty iterator? Hasnext returns false directly .. This does not affect the caller.

Vi. Summary

The tree hierarchy provided by the combination mode allows us to treat a single object and an object set equally (convenient operation ), however, this benefit is calculated by sacrificing the single responsibility principle of the class. In addition, the combination mode is implemented by inheritance and lacks elasticity.

Therefore, when using the combination mode, consider whether such sacrifice is worthwhile. If not, consider whether it can be replaced by other design modes ..

7. A single digress (about whether to throw an exception)

In some cases, we can choose to return NULL, false, and error code instead of throwing an exception. These methods may be more harmonious, but throwing an exception is sometimes the most appropriate expression of facts.

For example, assume that our file class has a haslinkedexe attribute, which indicates whether there is an application associated with it. Folder does not support the haslinkedexe attribute, at the same time, this property is inherited from the parent class and cannot be deleted.

In this case, we can choose to return false or throw an exception:

  • False: the folder is not associated with the application.
  • Throw an exception: Folder does not support this operation.

Obviously, the meaning of throwing an exception is what we really want to express.

-------

After talking about this, we spent a lot of effort to throw an exception. It seems that we just want to make it more appropriate?

No, no. Never think like this. A slight difference may cause serious problems, such:

Suppose we want to output all files that are not associated with the application (that is, "unknown file ")

If we used to return false to indicate that the folder does not support this operation, we will get the error result (all unknown files and all folders are output ..)

Composite pattern)

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.