Explain the structure of composite pattern and its application in Ruby design pattern programming

Time:2022-1-12

definition:It is also called composite mode, or part whole mode. It is mainly used to describe the relationship between part and whole, define and combine objects into a tree structure to represent the hierarchical structure of “part whole”, so that users can use single objects and combined objects consistently.

Class diagram:

Explain the structure of composite pattern and its application in Ruby design pattern programming

Role description:

Componnent abstract component role: define common methods and properties of objects participating in composition, and define some default behaviors or properties.
Leaf component: leaf object, under which there are no other branches, that is, the smallest unit of traversal.
Composite branch component: Branch object, which is used to combine branch nodes and leaf nodes to form a tree structure.

example:
I heard that your company has recently launched an e-book reading application, and the market response is very good. There is a Book Mall in the application, in which users can choose their favorite books at will. Your company also attaches great importance to this project, increases investment, and decides to add more functions to this application.
Well, you know you can’t escape this robbery. It wasn’t long before your leader found you. He told you that the current application makes statistics on the number of views and sales of each book, but now he wants to add the function of statistics on the number of views and sales of each book classification and the total number of views and sales of all books. I hope you can complete this function.
Of course, the work arranged by the leader can’t be shirked. You can only bite the bullet, but fortunately, this function doesn’t seem very complicated.
You prefer reading novels, so start with the statistical function of novels. First, get_ all_ The novels method can get all the novel names, and then pass the novel names into get_ browse_ The count method can get the number of views of the book and pass the novel name into get_ sale_ The count method can get the sales volume of the book. At present, you only have these known APIs to use, so start!

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def get_novels_browse_count
  browse_count = 0
  all_novels = get_all_novels()
  all_novels.each do |novel|
    browse_count += get_browse_count(novel)
  end
  browse_count
end
 
def get_novels_sale_count
  sale_count = 0
  all_novels = get_all_novels()
  all_novels.each do |novel|
    sale_count += get_browse_count(novel)
  end
  sale_count
end

Soon you wrote down the above two methods. Both methods get all the novel names, then calculate the views and sales of each novel one by one, and finally add the results to get the total amount.
The statistics of novels are completed, and then you start to do the statistics function of computer books. The code is as follows:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def get_computer_books_browse_count
  browse_count = 0
  all_computer_books = get_all_computer_books()
  all_computer_books.each do |computer_book|
    browse_count += get_browse_count(computer_book)
  end
  browse_count
end
 
def get_computer_books_sale_count
  sale_count = 0
  all_computer_books = get_all_computer_books()
  all_computer_books.each do |computer_book|
    sale_count += get_browse_count(computer_book)
  end
  sale_count
end

In addition to using get_ all_ computer_ The books method obtains all computer book titles. Other codes are basically the same as those in the novel statistics.
Now you have completed the statistical functions of two types of books, followed by medical, natural, historical, legal, political, philosophical, tourism, food and so on. You suddenly realize the seriousness of some problems. A large workload is nothing, but if you continue to write like this, your methods will explode. So many methods can’t be seen. Don’t mention how to use them.
At this time, you have to ask your leader for help and explain your confusion to him. I saw your leader think for a moment, and then confidently told you that using the combination mode can not only easily eliminate your confusion, but also complete the function excellently.
He immediately showed you the coding operation. First, he defined a statistics class with two methods:

?
1
2
3
4
5
6
7
8
9
10
11
class Statistics
   
  def get_browse_count
    raise "You should override this method in subclass."
  end
   
  def get_sale_count
    raise "You should override this method in subclass."
  end
   
end

Both methods simply throw an exception because they need to be overridden in subclasses.
Then define a novelstatistics class for statistical novel books, inherit the statistics class just defined, and override the two methods in Statistics:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class NovelStatistics < Statistics
 
  def get_browse_count
    browse_count = 0
    all_novels = get_all_novels()
    all_novels.each do |novel|
      browse_count += get_browse_count(novel)
    end
    browse_count
  end
   
  def get_sale_count
    sale_count = 0
    all_novels = get_all_novels()
    all_novels.each do |novel|
      sale_count += get_browse_count(novel)
    end
    sale_count
  end
 
end

In these two methods, the views and sales of novel books are counted respectively. In the same way, your leader defines a computerbookstatistics class to count the number of views and sales of computer books:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class ComputerBookStatistics < Statistics
 
  def get_browse_count
    browse_count = 0
    all_computer_books = get_all_computer_books()
    all_computer_books.each do |computer_book|
      browse_count += get_browse_count(computer_book)
    end
    browse_count
  end
   
  def get_sale_count
    sale_count = 0
    all_computer_books = get_all_computer_books()
    all_computer_books.each do |computer_book|
      sale_count += get_browse_count(computer_book)
    end
    sale_count
  end
 
end

In this way, the specific statistical implementation will be scattered in various classes, and there will be no explosion of the method you just described. But it hasn’t really started using the combination mode yet. The good play is still ahead, your leader boasted.

Define a medicalbookstatistics class to inherit statistics, which is used to count the number of views and sales of medical books. The code is as follows:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class MedicalBookStatistics < Statistics
 
  def get_browse_count
    browse_count = 0
    all_medical_books = get_all_medical_books()
    all_medical_books.each do |medical_book|
      browse_count += get_browse_count(medical_book)
    end
    browse_count
  end
   
  def get_sale_count
    sale_count = 0
    all_medical_books = get_all_medical_books()
    all_medical_books.each do |medical_book|
      sale_count += get_browse_count(medical_book)
    end
    sale_count
  end
 
end

I don’t know if you found it. Computer books and medical books are actually science and technology books. They can be combined together. At this time, your leader defines a technicalstatistics class to count the combined books of science and technology:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class TechnicalStatistics < Statistics
 
  def initialize
    @statistics = []
    @statistics << ComputerBookStatistics.new
    @statistics << MedicalBookStatistics.new
  end
 
  def get_browse_count
    browse_count = 0
    @statistics.each do |s|
      browse_count += s.get_browse_count
    end
    browse_count
  end
   
  def get_sale_count
    sale_count = 0
    @statistics.each do |s|
      sale_count += s.get_sale_count
    end
    sale_count
  end
 
end

You can see that because this class is a composite class, it is still quite different from the previous classes. Firstly, there is a constructor in technical statistics. In the constructor, computer books and medical books are added to the statistics array as subcategories, and then they are respectively added in get_ browse_ Count and get_ sale_ Count method traverses all sub categories, calculates their respective views and sales, and then adds them to get the total return.
The expansibility of the combination mode is very good. There are no rules and regulations. You can combine as you want. For example, all books are combined by various categories. Your leader immediately showed you the way to count the views and sales of all books.
Define an allstatistics class to inherit statistics. The specific code is as follows:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class AllStatistics < Statistics
 
  def initialize
    @statistics = []
    @statistics << NovelStatistics.new
    @statistics << TechnicalStatistics.new
  end
 
  def get_browse_count
    browse_count = 0
    @statistics.each do |s|
      browse_count += s.get_browse_count
    end
    browse_count
  end
   
  def get_sale_count
    sale_count = 0
    @statistics.each do |s|
      sale_count += s.get_sale_count
    end
    sale_count
  end
 
end

In the constructor of allstatistics, add novel books and science and technology books as sub categories to the statistics array. At present, you have only written these categories. Then use the same method in get_ browse_ Count and get_ sale_ Count method to count the number of views and sales of all books.
The schematic diagram of the current composite structure is as follows:

Explain the structure of composite pattern and its application in Ruby design pattern programming

Now you can easily get the views and sales volume of any classified books. For example, to get the views of science and technology books, you only need to call:

?
1
TechnicalStatistics.new.get_browse_count

To get the total sales of all books, you only need to call:

?
1
AllStatistics.new.get_sale_count

Of course, you can also change the composition structure at will, add various sub classification books, and the sub classification hierarchy can be as deep as you want. As mentioned earlier, the scalability of the composition mode is very good.
Your leader tells you that the code he wrote is highly repetitive. In fact, it can be optimized to remove the redundant code. Of course, this task is left to you. Your leader is a busy man and has long run away.

summary

Advantages of combined mode:
It can flexibly combine the concerns between local objects and overall objects. For the client, there is no difference between the calls of local objects and overall objects, making the call simple.

Disadvantages of combination mode:
1. The cost of combination operation is very high. If there are many sub objects in an object tree, a simple call may crash the system;
2. The problem of object persistence. The composite schema is a tree structure, which can not store data well in the relational database, but it is very suitable for XML persistence.

Applicable scenarios of combination mode:
1. Maintenance and presentation part – scenarios of overall relationship, such as tree menu, file and folder management.
2. Scenarios that can separate some modules or functions from a whole.