The example explains how Ruby uses the decorator mode in the design mode, and the ruby design mode.
Overview
If you are engaged in object-oriented development, adding behavior to a class or object and using inheritance mechanisms, this is a basic feature of all object-oriented languages. If an existing class lacks some methods, or you need to add more functions (CHARM) to the method ), you may just inherit this class to generate a new class-this is built on additional code.
By inheriting an existing class, the subclass can have its own method and its parent class method. However, this method is static, and users cannot control the ways and opportunities for adding behaviors. What if you want to change the behavior of an initialized object? Or, what should you do if you want to inherit the behavior of many classes? The first one can only be completed at runtime, which is obviously possible, but may lead to a large number of different classes-terrible things.
Problem
How do you organize your code so that it can easily add basic or few features that are rarely used, rather than directly writing no additional code inside your class?
Solution
- Dynamically add some additional responsibilities or actions to an object. The Decorator mode is more flexible than the subclass generation function.
- Provides a flexible solution for changing sub-classes. The decorator mode dynamically extends the functions of an object without changing the original class file and using inheritance. It creates a packaging object, that is, decoration, to package a real object.
- The decorator mode is more useful when used in a group of child classes. If you have a subclass (derived from a parent class), you need to add additional features when it is used independently of the subclass. You can use the decorator mode, to avoid code duplication and increase in the number of sub-classes.
Applicability
Decorator Mode
- Add roles to a single object dynamically and transparently without affecting other objects.
- Handle unrecoverable responsibilities.
- When the subclass generation method cannot be used for expansion. One case is that there may be a large number of independent extensions,
To support each combination, a large number of child classes are generated, which leads to explosive growth in the number of child classes.
Another scenario is that the class definition is hidden, or the class definition cannot be used to generate a subclass.
Instance
class SimpleWriter
def initialize (path)
@file = File.open (path, "w")
end
def write_line (line)
@ file.print (line)
@ file.print ("\ n")
end
#Number of characters
def pos
@ file.pos
end
#It will point the file pointer to the beginning of the file
def rewind
@ file.rewind
end
def close
@ file.colse
end
end
sw = SimpleWriter.new ("test.txt")
sw.write_line ("Hello")
puts sw.pos
puts sw.rewind
#Base class
class WriterDecorator
def initialize (real_writer)
@real_writer = real_writer
end
def write_line
@ real_writer.write_line
end
def pos
@ real_writer.pos
end
def rewind
@ real_writer.rewind
end
def close
@ real_writer.close
end
end
class NumberingWriter <WriterDecorator
attr: line_number
def initialize (real_writer)
super (real_writer)
@line_number = 1
end
#What is actually called is the write_line method of WriterDecorator, just adding the number (decoration) before the content written
#So NumberingWriter decorated the interface wirte_line of WriterDecorator
##
def write_line (line)
@ real_writer.write_line ("# {@ line_number}: # {line}")
@line_number + = 1
end
end
sw = SimpleWriter.new ("numbering_write.txt")
nw = NumberingWriter.new (sw)
nw.write_line ("hello, world")
nw.write_line ("hello, ruby")
puts nw.line_number
class CheckSummingWriter <WriterDecorator
attr_reader: check_num
def initialize (real_writer)
super (real_writer)
@check_num = 0
end
def write_line (line)
line.each_byte {| byte | @check_num + = byte% 256}
@ real_writer.write_line (line)
end
end
sw = SimpleWriter.new ("check_num_writer.txt")
csw = CheckSummingWriter.new (sw)
csw.write_line ("hello, world")
puts csw.check_num
class TimeStampingWriter <WriterDecorator
def initialize (real_writer)
super (real_writer)
end
def write_line (line)
@ real_writer.write_line ("# {Time.now}: # {line}")
end
end
# Inverted looking
# 5. The actual call is SimpleWriter get write_line method, write content to the file
sw = SimpleWriter.new ("mix.txt")
# 4. The NumberingWriter write_line method is actually called, and the number is added before the input data
# Then pass to @real_writer, @real_witer at this time is sw
nw = NumberingWriter.new (sw)
# 3. The actual call to the Write_line method of TimeStampingWriter adds a time stamp to the input
# Then pass to @real_writer, @real_witer at this time is nw
tsw = TimeStampingWriter.new (nw)
# 2. The CheckSummingWriter write_line method is actually called, and the byte count of the input data
# Then pass it to @real_writer, @real_witer at this time is tsw
csw = CheckSummingWriter.new (tsw)
# 1. Csw calls write_line
csw.write_line ("hello, world")
puts csw.check_num
Two ruby-style decoration Models
(1) Use extend to mix modules
class SimpleWriter
def initialize (path)
@file = File.open (path, "w")
end
def write_line (line)
@ file.print (line)
@ file.print ("\ n")
end
#Characters
def pos
@ file.pos
end
#It will point the file pointer to the beginning of the file
def rewind
@ file.rewind
end
def close
@ file.colse
end
end
#Use the extend method to dynamically mix modules for decoration
module TimeStampingWriter
def write_line (line)
super ("# {Time.now}: # {line}")
end
end
module NumberingWriter
attr_reader: line_number
def write_line (line)
@line_number = 1 unless @line_number
super ("# {@ line_number}: # {line}")
@line_number + = 1
end
end
The added module is called first, and then the write_line method of the parent class is called through super.
In this example, add a timestamp before the text, add a number, and finally write the file
sw = SimpleWriter.new("out3.txt")
sw.extend(NumberingWriter)
sw.extend(TimeStampingWriter)
sw.write_line("hello,ruby")
(2) Use the alias keyword
class SimpleWriter
def initialize (path)
@file = File.open (path, "w")
end
def write_line (line)
@ file.print (line)
@ file.print ("\ n")
end
#Characters
def pos
@ file.pos
end
#It will point the file pointer to the beginning of the file
def rewind
@ file.rewind
end
def close
@ file.colse
end
end
Ruby implements another dynamic way of decorating patterns:
Modify the instance method of the object, so a timestamp will be added to the out1.txt file without affecting the object sw2, and no timestamp will be added to out2.txt.
sw1 = SimpleWriter.new ("out1.txt")
class << sw1
alias old_write_line write_line
def write_line (line)
old_write_line ("# {Time.now}: # {line}")
end
end
sw1.write_line ("hello, world")
sw2 = SimpleWriter.new ("out2.txt")
sw2.write_line ("hello, world")
Articles you may be interested in:
- Detailed description of the structure of the Combination Mode and Its Application in Ruby Design Mode Programming
- The template method in the design mode applies two examples in Ruby.
- Example parsing: Use of Strategy Mode in Ruby Design Mode Programming
- Example of using Builder mode in Ruby Design Mode Programming
- Detailed description of the Application of Singleton mode in Ruby Design Mode Programming
- Programming in Ruby design mode-Introduction to adapter Mode
- Ruby uses code instances in the proxy mode and decoration mode in the Design Mode
- Ruby uses the simple factory mode and factory method mode in the Design Mode
- Application instance analysis of appearance mode in Ruby Design Mode Programming