LoginSignup
1
2

More than 5 years have passed since last update.

Rubyデザインパターン - 構造に関するパターン

Last updated at Posted at 2017-04-02

Adapter

概要

既存のクラスの出力やインターフェースを必要な形式に変換する

例:

IPhone情報を出力するRenderクラスがある
Renderクラスを使ってAndroid情報を出力したい

  • IPhone(Adaptee)
  • AndroidAdapter(Adapter)
  • Android(Client)

メリット

Adapterを作成すると、Android、IPhoneクラスのどちらも修正しなくて済む

方法1

委譲による実現
IPhoneのオブジェクトをAndroidAdapterに保持させる

class Render 
  def render(text_object) 
    @text = text_object.text
    @size = text_object.size_cm
    @color = text_object.color
    printData
  end

  private
    def printData
      puts @text
      puts "#{@size} cm"
      puts @color
    end
end

class IPhone
  attr_reader :text, :size_cm, :color

  def initialize(text, size_cm, color)
    @text = text
    @size_cm = size_cm
    @color = color
  end
end

class Android
  attr_reader :string, :size_mm, :type

  def initialize(string, size_mm, type)
    @string = string
    @size_mm = size_mm
    @type = type
  end
end

class AndroidAdapter
  def initialize(android)
    @android = android
  end

  def text
    @android.string
  end

  def size_cm
    @android.size_mm / 1000
  end

  def color
    @android.type
  end
end

iphone = IPhone.new("This is IPhone", 12, "red")
android = Android.new("This is android", 15000, "black")
fixed_android = AndroidAdapter.new(android)

render_object = Render.new
p "---IPhone---"
render_object.render(iphone)
p "---Android---"
render_object.render(fixed_android)

方法2

継承による実現
Androidに新しいインターフェースを記述

android = Android.new("This is android", 15000, "black")

class << android
  def color
    type
  end

  def text
    string
  end

  def size_cm
      size = size_mm / 1000
      "#{size} cm"
  end
end

render_object = Render.new
p "---Android---"
p android.text
p android.size_cm
p android.color

Composite

概要

ファイルシステムなどの再帰的なデータ構造を表現を楽にする
要素とそれらをまとめて管理するものが似たようなメソッドを持っていて、どちらも同じように扱いたいとき

# 共通メソッドを規定(Component)
class Entry
  def get_name; end
  def ls(prefix) end
  def remove; end
end

# 単純な構成要素、再帰しない(Leaf)
class FileClass < Entry
  def initialize(name)
    @name = name
  end
  def get_name
    @name
  end
  def ls(prefix)
    puts(prefix + "/" + get_name)
  end
  def remove
    puts "Removed " + @name + " file"
  end
end

# 再帰する(Composite)
class DirClass < Entry
  def initialize(name)
    @name = name
    @directory = Array.new
  end
  def get_name
    @name
  end
  # ディレクトリにディレクトリ/ファイルを追加する
  def add(entry)
    @directory.push(entry)
  end
  # ファイル/ディレクトリのパスを返す
  def ls(prefix)
    puts(prefix + "/" + get_name)
    @directory.each do |element|
      element.ls(prefix + "/" + @name)
    end
  end
  def remove
    @directory.each do |element|
      element.remove
    end
    puts "Removed " + @name + " directory"
  end
end

root = DirClass.new("rootDir")
tmp = DirClass.new("tmpDir")
tmp.add(FileClass.new("confFile"))
tmp.add(DirClass.new("dataDir"))
root.add(tmp)

root.ls("")
# /rootDir
# /rootDir/tmpDir
# /rootDir/tmpDir/confFile
# /rootDir/tmpDir/dataDir

root.remove
# Removed confFile file
# Removed dataDir directory
# Removed tmpDir directory
# Removed rootDir directory

Decorator

概要

既存のオブジェクトに対して簡単に機能の追加をする
既存のオブジェクトの変更をしない

require 'forwardable'
# ConcreteComponent
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
# Decorator(共通部分)
class WriterDecorator
  extend Forwardable
  def_delegators :@real_writer, :write_line, :pos, :rewind, :close
  def initialize(real_writer)
    @real_writer = real_writer
  end
end
# Decorator
module NumberingWriter
  attr_reader :line_number

  def write_line(line)
    @line_number = 1 unless @line_number
    super("#{@line_number} : #{line}")
    @line_number += 1
  end
end
# Decorator
module TimeStampingWriter
  def write_line(line)
    super("#{Time.new} : #{line}")
  end
end

f = SimpleWriter.new('88.txt')
f.extend TimeStampingWriter
f.extend NumberingWriter
f.write_line('Hello out there')
f.close

Proxy

概要

プログラム本体のクラスの前にもう一つクラスを咬ませて、データアクセスそのものとそれに付随する処理を分離させる

class RealObject
  attr_accessor :secret_info
  def initialize(secret_info)
    @secret_info = secret_info
  end
end

class Proxy
  attr_accessor :real_object
  def initialize(real_object)
    @real_object = real_object
  end
  def secret_info
    print "warning use secret_infoz\n"
    return @real_object.secret_info
  end
end

p = Proxy.new(RealObject.new("secret"))
print p.secret_info
1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2