Design Pattern - Decorate
Bài đăng này đã không được cập nhật trong 3 năm
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("\n")
end
def checksumming_write_line(data)
data.each_byte {|byte| @check_sum = (@check_sum + byte) % 256 }
@check_sum += "\n"[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("\n")
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à SimpleWriter
trong 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
Bài viết liên quan:
All rights reserved