class Bulb def turnOn puts "電気がつきました" end def turnOff puts "真っ暗です" end end class Command def execute end def undo end def redo end end class TurnOn < Command def initialize(bulb) @bulb = bulb end def execute @bulb.turnOn end def undo @bulb.turnOff end def redo execute end end class TrunOff < Command def initialize(bulb) @bulb = bulb end def execute @bulb.turnOff end def undo @bulb.turnOn end def redo execute end end class RemoteControll def submit(command) @command = command.execute end end bulb = Bulb.new turnOn = TurnOn.new(bulb) turnOff = TrunOff.new(bulb) remote_controll = RemoteControll.new remote_controll.submit(turnOn) remote_controll.submit(turnOff) #出力 #電気がつきました #真っ暗です
オブジェクト指向
「人の認識に近い形で」というところが一般的に言われる「責務に応じた形で」と同じニュアンスです。
rubyでデザインパターン compositeパターン
あるオブジェクトの集合で構成されたオブジェクトも同一のインターフェースをもつパターンです。
class Employee attr_reader :salary, :name def initialize(name, salary) @name = name @salary = salary end end class Developer < Employee attr_reader :role def initialize(name, salary, role) super(name, salary) @role = role end end class Designer < Employee attr_reader :role def initialize(name, salary, role) super(name, salary) @role = role end end class Organization def initialize @Employees = [] end def addEmployee(employee) @Employees << employee end def salary @NetSalary = 0 @Employees.each do |employee| @NetSalary = @NetSalary + employee.salary end @NetSalary end end John = Developer.new('John', 20, 'developer') Aya = Designer.new('Aya', 18, 'designer') organizatin = Organization.new organizatin.addEmployee(John) organizatin.addEmployee(Aya) puts John.salary puts Aya.salary puts organizatin.salary #出力 #20 #18 #38
rubyでデザインパターン adapterパターン
ライオンを狩るハンタークラスがあったのですが
急遽、野犬も狩るように言われてしまいました。
しかし、野犬はライオンとは別のインターフェイスを持っているので
このままではうまく使えません。
そこで野犬クラスを包み込んだようなadapterクラスを用意して中継する感じです。
このパターンは仕方なくって感じがしますね・・・
もちろん初めから野犬も狩猟の対象になることが分かっていたのなら野犬も同じインターフェイスを備えるべきです。
class Lion def roar end end class AfricanLion < Lion def roar "がおー" end end class Hunter def hunt(target) puts "ターゲットは#{target.roar}と威嚇している!" end end class WildDog def bark "わん" end end class WildDOgAdapter < Lion def initialize(dog) @dog = dog end def roar @dog.bark end end african_lion = AfricanLion.new wild_dog = WildDog.new wild_dog_adapter = WildDOgAdapter.new(wild_dog) hunter = Hunter.new hunter.hunt(african_lion) hunter.hunt(wild_dog_adapter) #出力 #ターゲットはがおーと威嚇している! #ターゲットはわんと威嚇している! #
rubyでデザインパターン decoratorパターン
class Coffee #価格 def getCost end #商品説明 def getDescription end end class SimpleCoffee < Coffee def getCost 10 end def getDescription 'Simple coffee' end end class MilkCoffee < Coffee def initialize(coffee) @coffee = coffee end def getCost @coffee.getCost + 2 end def getDescription "#{@coffee.getDescription}にミルクを加えました。" end end simple_coffee = SimpleCoffee.new milk_coffee = MilkCoffee.new(simple_coffee) puts simple_coffee.getCost puts milk_coffee.getCost puts simple_coffee.getDescription puts milk_coffee.getDescription #こうやって出力されます #10 #12 #Simple coffee #Simple coffeeにミルクを加えました。
このように基本はSimpleCoffeeだけど、ときどき機能を拡張するみたいな時に使えるパターン
Rubyでデザインパターン proxyパターン
class Door def open puts 'open door!' end def close puts 'close door!' end end class SecurityDoor def initialize(door) @door = door end def open(password) if password == 'open goma!' @door.open else puts 'password fail' end end def close(password) if password == 'close goma!' @door.close else puts 'password fail' end end end door = Door.new security_door = SecurityDoor.new(door) security_door.open('valid_password!') security_door.open('open goma!')
DoorクラスをラップするSecurityDoorクラスを定義しています。
そこでパスワードを確認する処理を挟んで本来アクセスしたいであろうDoorクラスへのアクセスを制御しています。
ここがポイントでアクセス制御がproxyパターンの目的です。
場合によってはdecoratorパターンと実装がかなり似ることがありますが目的が違います。
proxyパターンの目的はアクセス制御
decoratorパターンの目的は機能の追加です。
Rubyによるオブジェクト指向 8章 コードサンプル
attr_reader :size, :parts def initialize(args={}) @size = args[:size] @parts = args[:parts] end def spares parts.spares end end class Part attr_reader :name, :description, :needs_spares def initialize(args) @name = args[:name] @description = args[:description] @needs_spares = args.fetch(:needs_spares, true) end end class Parts attr_reader :parts def initialize(parts) @parts = parts end def spares parts.select { |part| part.needs_spares } end end chain = Part.new(name: "chain", description: "10-speed") road_tire = Part.new(name: 'tire_size', description: "23") road_parts = Parts.new([chain, road_tire]) road_bike = Bycycle.new( size: 'L', parts: road_parts ) puts road_bike.size puts road_bike.spares.size