最近讀書會在讀深入淺出設計模式,趁這個機會複習一下設計模式,試著舉出簡單的例子並且用非模式與模式的方式來實作,比較它們的差異與優缺點。
我們有一個機器人競技場(RobotAerna)會進行機器人比賽,在比賽的一開始會先進行建立機器人的動作。一個機器人(Robot)由身體(RobotBody)、頭(RobotHead)與腳(RobotFoot)組成,另外有一個 name 的 method 會顯示目前的組合與另一個 add_battery 的 method 做安裝電池的動作,如下所示:
class Robot
attr_accessor :head, :body, :foot
def name
"I'm a #{head.name}, #{body.name}, #{foot.name} robot."
end
def add_battery
@battery = 'Standard AAA Battery'
end
end
而機器人的身體、頭與腳又有各種不同的種類:
class RobotHead
def name
fail 'You should implement "name" in your RobotHead-based class.'
end
end
class IronHead < RobotHead
def name
'Iron-Head'
end
end
class WoodHead < RobotHead
def name
'Wood-Head'
end
end
class NoHead < RobotHead
def name
''
end
end
class RobotBody
def name
fail 'You should implement "name" in your RobotBody-based class.'
end
end
class IronBody < RobotBody
def name
'Iron-Body'
end
end
class WoodBody < RobotBody
def name
'Wood-Body'
end
end
class RobotFoot
def name
fail 'You should implement "name" in your RobotFoot-based class.'
end
end
class TwoLeggedFoot < RobotFoot
def name
'Two-Legged'
end
end
class WheelFoot < RobotFoot
def name
'Wheel'
end
end
class RocketFoot < RobotFoot
def name
'Rocket'
end
end
現在的問題是要實作機器人競技場建立機器人的部分,需要建立的機器人如下:
組裝的方式就是先建立一個 robot 的 instance,接著建立對應的 body, head, foot 的 instance 並設定到 robot 的 body, head 與 foot。組裝好機器人之後,還要記得安裝電池(add_battery)才會動。
class RobotArena
def initialize
@robots = []
@robots << create_iron_robot
@robots << create_rocket_robot
end
def create_iron_robot
robot = Robot.new
robot.body = IronBody.new
robot.head = IronHead.new
robot.foot = TwoLeggedFoot.new
robot.add_battery
robot
end
def create_rocket_robot
robot = Robot.new
robot.body = WoodBody.new
robot.head = WoodHead.new
robot.foot = RocketFoot.new
robot.add_battery
robot
end
def roll_call
@robots.each do |robot|
p robot.name
end
end
end
arena = RobotArena.new
arena.roll_call
class RobotFactory
def create
robot = Robot.new
add_body(robot)
add_head(robot)
add_foot(robot)
robot.add_battery
robot
end
def add_body(robot)
fail 'You should implement "add_body" in your RobotFactory-based class.'
end
def add_head(robot)
fail 'You should implement "add_head" in your RobotFactory-based class.'
end
def add_foot(robot)
fail 'You should implement "add_foot" in your RobotFactory-based class.'
end
end
class IronRobotFactory < RobotFactory
def add_body(robot)
robot.body = IronBody.new
end
def add_head(robot)
robot.head = IronHead.new
end
def add_foot(robot)
robot.foot = TwoLeggedFoot.new
end
end
class RocketRobotFactory < RobotFactory
def add_body(robot)
robot.body = WoodBody.new
end
def add_head(robot)
robot.head = WoodHead.new
end
def add_foot(robot)
robot.foot = RocketFoot.new
end
end
class RobotArena
def initialize
@robots = []
@robots << create_iron_robot
@robots << create_rocket_robot
end
def create_iron_robot
factory = IronRobotFactory.new
factory.create
end
def create_rocket_robot
factory = RocketRobotFactory.new
factory.create
end
def roll_call
@robots.each do |robot|
p robot.name
end
end
end
arena = RobotArena.new
arena.roll_call
Abstract Factory - 抽象工廠模式
對於一個需要比較複雜建立流程的 class(Robot),使用一個抽象工廠(RobotFactory)定義整體建立物件的流程(RobotFactory#create),而不同種類物件(robot)所需的不同建立流程(add_body, add_head, add_foot)則由工廠的子類別(IronRobotFactory, RocketRobotFactory)各自定義客製化。client(RobotArena)端只需要使用對應的工廠(IronRobotFactory, RocketRobotFactory)建立出需要的物件(robot)而不用去了解物件本身建立的細節。
當建立一個物件(robot)需要複雜的流程,尤其是物件中包含了其它的零件(RobotHead, RobotBody, RobotFoot)的組合,而且會根據不同種類的物件會有不同的建立方式(add_body, add_head, add_foot)。