定义:把使用哪个类的决定放在子类的技巧成为工厂方法模式
元素:在工厂方法模式中有两个元素,第一个元素是创造者,第二个元素是产品,其中创造者有父类和以及继承父类的子类组成,由子类决定生产哪个生产者对象。在下面的uml中,factory是生产者,product是产品。
本质:工厂方法模式的核心是模板方法模式在创建对象问题上的应用。
uml:
#代码简化演示
class Duck
def eat
puts "duck is eating"
end
end
class Frog
def eat
puts "frog is eating"
end
end
class Pond
def initialize
@pond = new_animal
end
def simulate
@pond.eat
end
end
class DuckPond < Pond
def new_animal
Duck.new
end
end
class FrogPond < Pond
def new_animal
Frog.new
end
end
duck_pond = DuckPond.new
duck_pond.simulate
上述代码分析:上面的代码中Pond是生产者,该生产者是基类,该基类定义了生成对象的抽象方法,它有两个子类,使用两个子类来具体决定产生那个类对象。
上面的代码是仅仅只有动物一种类型的情况下,如果随着代码增加,需要增加植物等其他类型,需要新建一个new plant方法,随着类型的增多的类似new plant方法,为了免除增加方法的烦恼,下面使用一次性的new organism来替代new plant和new animal
#增加new_organism来替代new_animal
class Duck
def eat
puts "duck eat"
end
end
class Frog
def eat
puts "frog eat"
end
end
class WaterLily
def grow
puts "waterlily grow"
end
end
class Algae
def grow
puts "algae grow"
end
end
class Pond
def initialize
@animal = new_organism(:animal)
@plant = new_organism(:plant)
end
def simulate
@animal.eat
@plant.grow
end
end
class DuckWaterLilyPond < Pond
def new_organism(type)
if type == :animal
Duck.new
elsif type == :plant
WaterLily.new
else
raise "unknown organism type #{type}"
end
end
end
class FrogAlgaePond < Pond
def new_organism(type)
if type == :animal
Frog.new
elsif type == :plant
Algae.new
else
raise "unknown organism type #{type}"
end
end
end
one = DuckWaterLilyPond.new
one.simulate
two = FrogAlgaePond.new
two.simulate
上面代码的问题是需要为了生成一些类型必须写很多子类,比如DuckWaterLilyPond和FrogAlgaePond,可以通过将Frog, Duck, WaterLily, Algae放置到变量中来消除子类的继承,其实这些类也只是对象,作用是用来产生其他对象的对象。
#消除子类
class Duck
def eat
puts "duck eat"
end
end
class Frog
def eat
puts "frog eat"
end
end
class WaterLily
def grow
puts "waterlily grow"
end
end
class Algae
def grow
puts "algae grow"
end
end
class Pond
def initialize(animal_class, plant_class)
@animal_class = animal_class
@plant_class = plant_class
@animal = new_organism(:animal)
@plant = new_organism(:plant)
end
def simulate
@animal.eat
@plant.grow
end
def new_organism(type)
if type == :animal
@animal_class.new
elsif type == :plant
@plant_class.new
else
raise "unknown organism type #{type}"
end
end
end
one = Pond.new(Duck, WaterLily)
one.simulate