最近讀書會在讀深入淺出設計模式,趁這個機會複習一下設計模式,試著舉出簡單的例子並且用非模式與模式的方式來實作,比較它們的差異與優缺點。
我們有一個訊息轉發系統(MessageDeliver) 與加密系統(Encrypter)
,它們會接收傳入訊息並轉發給負責輸出的裝置(Exporter),其中負責輸出的裝置(Exporter)有三個:螢幕顯示裝置(ScreenDisplay)
、列印裝置(Printer)
與寄信裝置(Mailer)
,每個 exporter 都有一個 output(msg) 的 method 用來輸出訊息。
程式碼如下:
class Exporter
def output(msg)
fail 'You should implement "output" in your Exporter-based class.'
end
end
class ScreenDisplay < Exporter
def output(msg)
"The message '#{msg}' is displayed on the screen."
end
end
class Printer < Exporter
def output(msg)
"The message '#{msg}' is printed."
end
end
class Mailer < Exporter
def output(msg)
"The message '#{msg}' is mailed."
end
end
現在的問題是要實作訊息轉發與加密系統,這兩個系統有下列的行為:
class MessageDeliver
def deliver(msg, target)
exporter = case target
when :screen_display then ScreenDisplay.new
when :printer then Printer.new
when :mailer then Mailer.new
else
fail 'Uknown exporter.'
end
exporter.output(msg)
end
end
class Encrypter
def encrypt(msg, target)
msg = msg.gsub(/[a-zA-Z0-9]/, '*')
exporter = case target
when :screen_display then ScreenDisplay.new
when :printer then Printer.new
when :mailer then Mailer.new
else
fail 'Uknown exporter.'
end
exporter.output(msg)
end
end
msg_deliver = MessageDeliver.new
encrypter = Encrypter.new
msg_deliver.deliver('hello', :screen_display)
msg_deliver.deliver('Yoooo~~~', :mailer)
encrypter.encrypt('A secret message.', :mailer)
class ExporterFactory
def self.create(target)
case target
when :screen_display then ScreenDisplay.new
when :printer then Printer.new
when :mailer then Mailer.new
else
fail 'Uknown exporter.'
end
end
end
class MessageDeliver
def deliver(msg, target)
exporter = ExporterFactory.create(target)
exporter.output(msg)
end
end
class Encrypter
def encrypt(msg, target)
msg = msg.gsub(/[a-zA-Z0-9]/, '*')
exporter = ExporterFactory.create(target)
exporter.output(msg)
end
end
msg_deliver = MessageDeliver.new
encrypter = Encrypter.new
msg_deliver.deliver('hello', :screen_display)
msg_deliver.deliver('Yoooo~~~', :mailer)
encrypter.encrypt('A secret message.', :mailer)
加密方式每個 exporter 變得不同,需要另外三個 exporter(EncryptedScreenDisplay, EncryptedPrinter, EncryptedMailer) 各別處理加密如下:
class EncryptedScreenDisplay < Exporter
def output(msg)
msg = msg.gsub(/[a-zA-Z0-9]/, '*')
"The message '#{msg}' is displayed on the screen."
end
end
class EncryptedPrinter < Exporter
def output(msg)
msg = msg.gsub(/[a-zA-Z0-9]/, 'o')
"The message '#{msg}' is printed."
end
end
class EncryptedMailer < Exporter
def output(msg)
msg = msg.gsub(/[a-zA-Z0-9]/, 'x')
"The message '#{msg}' is mailed."
end
end
class ExporterFactory
def self.create(target, encrypted = false)
if encrypted
return case target
when :screen_display then EncryptedScreenDisplay.new
when :printer then EncryptedPrinter.new
when :mailer then EncryptedMailer.new
else
fail 'Uknown exporter.'
end
else
return case target
when :screen_display then ScreenDisplay.new
when :printer then Printer.new
when :mailer then Mailer.new
else
fail 'Uknown exporter.'
end
end
end
end
class MessageDeliver
def deliver(msg, target)
exporter = ExporterFactory.create(target)
exporter.output(msg)
end
end
class Encrypter
def encrypt(msg, target)
exporter = ExporterFactory.create(target, true)
exporter.output(msg)
end
end
msg_deliver = MessageDeliver.new
encrypter = Encrypter.new
msg_deliver.deliver('hello', :screen_display)
msg_deliver.deliver('Yoooo~~~', :mailer)
encrypter.encrypt('A secret message.', :mailer)
class ExporterFactory
def create(target)
fail 'You should implement "create" in your ExporterFactory-based class.'
end
end
class NormalExporterFactory < ExporterFactory
def create(target)
return case target
when :screen_display then ScreenDisplay.new
when :printer then Printer.new
when :mailer then Mailer.new
else
fail 'Uknown exporter.'
end
end
end
class EncryptedExporterFactory < ExporterFactory
def create(target)
return case target
when :screen_display then EncryptedScreenDisplay.new
when :printer then EncryptedPrinter.new
when :mailer then EncryptedMailer.new
else
fail 'Uknown exporter.'
end
end
end
class MessageDeliver
def deliver(msg, target)
factory = NormalExporterFactory.new
exporter = factory.create(target)
exporter.output(msg)
end
end
class Encrypter
def encrypt(msg, target)
factory = EncryptedExporterFactory.new
exporter = factory.create(target)
exporter.output(msg)
end
end
msg_deliver = MessageDeliver.new
encrypter = Encrypter.new
msg_deliver.deliver('hello', :screen_display)
msg_deliver.deliver('Yoooo~~~', :mailer)
encrypter.encrypt('A secret message.', :mailer)
Factory Method - 工廠方法模式
將原本建立 class(exporter) 的流程封裝在 factory(ExporterFactory) class 之中,需要建立 class(exporter) 時,就使用 factory(ExporterFactory) 來建立。這樣的好處是透過 factory(ExporterFactory) 降低使用這些 class(exporter) 的 client(MessageDeliver, Encrypter) 與 class(exporter) 之間的相依性。
當有一系列的 class(exporter) 需要被建立,而建立的方式可能會根據不同的情況而需要建立不同的 class(exporter)。