Last active
December 29, 2018 03:51
-
-
Save david-pm/76c3e84ac390d091c1cc65653e06274e to your computer and use it in GitHub Desktop.
Protection Proxy Pattern example that respects Interface Segregation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class BankAccount | |
attr_reader :balance | |
def initialize(starting_balance) | |
@balance = starting_balance | |
end | |
# if this were #irl we'd need to worry about race conditions here | |
def deposit(amount) | |
@balance += amount | |
end | |
def withdraw(amount) | |
@balance -= amount | |
end | |
end | |
# The Etc module provides access to information typically stored in files | |
# in the /etc directory on Unix systems. | |
# require 'etc' | |
# one approach taken from the excellent book Design Patterns in Ruby by Russ Olsen# class AccountProtectionProxy | |
# this is great since we dont need to write proxy methods by hand, and this class will never need to change! | |
# | |
# class AccountProtectionProxy | |
# def initialize(real_account, owner_name) | |
# @real_account = real_account | |
# @owner_name = owner_name | |
# end | |
# def method_missing(name, *args) | |
# access_control! | |
# @real_account.send(name, *args) | |
# end | |
# private def access_control! | |
# raise("Access denied! #{Etc.getlogin} is unknown") unless Etc.getlogin == @owner_name | |
# end | |
# end | |
# the downside, in my opinion, is that this AccountProtectionProxy class will blindly | |
# proxy any object you give it | |
# | |
# the essence of the Interface Segregation Principle is that a client should be provided with an | |
# interface limited only to the methods needed in the context of the client's interaction | |
# | |
# so, let's be more explicit about what we want to expose but still obviate the need to | |
# write out each proxied method manually. we use the Forwardable module to do so: | |
require 'etc' | |
require 'forwardable' | |
class AccountProtectionProxy | |
extend Forwardable | |
def_delegators :@real_account, :balance, :deposit, :withdraw | |
def initialize(real_account, owner_name) | |
@real_account = real_account | |
@owner_name = owner_name | |
end | |
private def access_control! | |
raise("Access denied! #{Etc.getlogin} is unknown") unless Etc.getlogin == @owner_name | |
end | |
end | |
ba = BankAccount.new(100) | |
proxy = AccountProtectionProxy.new(ba, 'bob') | |
proxy.deposit 20 | |
puts proxy.balance | |
proxy.withdraw 120 | |
puts proxy.balance |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment