Design Pattern - Decorate
Decorate là gì? Một trong số những câu hỏi cơ bản của lập trình là: Làm thế nào có thể thêm được tính năng cho code hay chương trình của bạn mà không cần truyền thêm một mớ hỗ độn vào khiến nó cồng kềnh thêm và khó quản lý. Có một số design pattern sẽ giúp bạn làm được điều này khá tốt như: ...
Decorate là gì?
Một trong số những câu hỏi cơ bản của lập trình là: Làm thế nào có thể thêm được tính năng cho code hay chương trình của bạn mà không cần truyền thêm một mớ hỗ độn vào khiến nó cồng kềnh thêm và khó quản lý.
Có một số design pattern sẽ giúp bạn làm được điều này khá tốt như: Observer pattern, Composites và Iterators. Tuy nhiên nếu bạn chỉ muốn đơn giản là thay đổi một chút về cách làm việc hay hiển thị của chương trình thì Decorator pattern là lựa chọn hoàn hảo cho bạn.
Hãy tưởng tượng rằng bạn có một số văn bản cần phải được ghi vào một tập tin. Nghe có vẻ đơn giản, nhưng trong hệ thống của bạn đôi khi bạn chỉ muốn viết text đơn thuần thông thường, trong khi ở thời điểm khác bạn muốn đánh số mỗi dòng như nó khi xuất chúng ra. Đôi khi bạn muốn thêm một thời gian để mỗi dòng vào các tập tin. Đôi khi bạn cần một tổng kiểm tra từ các văn bản để sau này bạn có thể đảm bảo rằng nó đã được viết và lưu trữ đúng cách...
Chúng ta có đoạn code sau giúp xử lý vấn đề này:
class EnhancedWriter attr_reader :check_sum def initialize(path) @file = File.open(path, "w") @line_number = 1 end def write_line(line) @file.print(line) @file.print(" ") end def checksumming_write_line(data) data.each_byte {|byte| @check_sum = (@check_sum + byte) % 256 } @check_sum += " "[0] % 256 write_line(data) end def timestamping_write_line(data) write_line("#{Time.new}: #{data}") end def numbering_write_line(data) write_line("%{@line_number}: #{data}") @line_number += 1 end def close @file.close end end
Với đoạn code này, bạn có thể xuất ra dạng plain text
writer = EnhancedWriter.new('out.txt') writer.write_line("A plain line")
hay thêm checksum
writer.checksumming_write_line('A line with checksum') puts("Checksum is #{writer.check_sum}")
hoặc thêm timestamp ở mỗi dòng
writer.timestamping_write_line('with time stamp') writer.numbering_write_line('with line number')
Cách làm này đáp ứng được yêu cầu đặt ra nhưng có một vấn đề. Đó là phương pháp này ném tất cả mọi thứ vào 1 class.Nó sẽ khiến mọi thứ trở nên mất kiểm soát và đôi khi là thừa vì tất cả mọi thứ đều được ném vào, không cần biết nó có được sử dụng hay không.
Bạn có thể nghĩ tới việc tách chúng ta bằng cách sử dụng một base class và các sub class giống hình dưới
Việc sinh ra quá nhiều subclass kế thừa như vậy thực sự không thể bao quát hết tất cả những thay đổi những sự kết hợp khác nhau trong thiết kế. Có một giải pháp tốt hơn cho việc này.
Đầu tiên xây dựng một class có chức năng cơ bản
class SimpleWriter def initialize(path) @file = File.open(path, 'w') end def write_line(line) @file.print(line) @file.print(" ") end def pos @file.pos end def rewind @file.rewind end def close @file.close end end
Bây giờ nếu bạn muốn đánh số các dòng, hãy thêm vào 1 đối tượng trung gian giữa SimpleWriter và Client
class WriterDecorator def initialize(real_writer) @real_writer = real_writer end def write_line(line) @real_writer.write_line(line) end def pos @real_writer.pos end def rewind @real_writer.rewind end def close @real_writer.close end end class NumberingWriter < WriterDecorator def initialize(real_writer) super(real_writer) @line_number = 1 end def write_line(line) @real_writer.write_line("#{@line_number}: #{line}") @line_number += 1 end end
Class NumberingWriter thể hiện core interface tương tự như plain text, về cơ bản cả hai cách đều giống nhau. Ví dụ để đánh số dòng, chúng ta chỉ cần đặt SimpleWriter vào NumberingWriter:
writer = NumberingWriter.new(SimpleWriter.new('final.txt')) writer.write_line('Hello out there')
Formal Decorate
ConcreteComponent là object sẽ implement các chức năng cơ bản. Nó chính là SimpleWritertrong ví dụ trước.
Class Decorator thì tham chiếu tới một Component, và nó implement tất cả các phương thức của Component. Trong ví dụ, 3 class Decorate là NumberingWriter, CheckSummingWriter, TimeStampingWriter.
Mỗi Decorate sẽ tác động theo các cách khác nhau để biến đổi các method có sẵn.
Tham khảo
Github (updating):https://github.com/ducnhat1989/design-patterns-in-ruby
Sách: “DESIGN PATTERNS IN RUBY” của tác giả Russ Olsen
:
- CÁC NGUYÊN TẮC TRONG DESIGN PATTERN