Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

new files

  • Loading branch information...
commit 756b3a06dfe124e30afd23e2f8b6d3ec00d633c5 1 parent 19cd383
Alexander Shvets authored
4 behavioral/command2.rb
View
@@ -17,8 +17,8 @@ def operation()
# 2. test
-command_user = CommandUser.new {
+command_user = CommandUser.new do
puts "command1"
-}
+end
command_user.operation
4 creational/abstract-factory.rb
View
@@ -3,8 +3,8 @@
# Provides an interface for creationg families of related or dependent objects without specifying
# their concrete class.
#
-# Provides one level of interface higher than the factory pattern. It is used to return one
-# of several factories.
+# Provides one level of interface higher than the Factory Method pattern.
+# It is used to return one of the several factories.
# 1. common interface for type and it's specializations
66 creational/builder1.rb
View
@@ -0,0 +1,66 @@
+# builder1.rb
+
+# Separates the construction of a complex object from its representation so that the same
+# construction process can create different representations.
+
+# 1. type builder interface
+
+class ProductBuilder
+ def build_part1
+ end
+
+ def build_part2
+ end
+
+ def build_part3
+ end
+
+end
+
+# 2. type builder implementations
+
+class ComputerBuilder < ProductBuilder
+ def build_part1
+ puts "Building part1: motherboard"
+ end
+
+ def build_part2
+ puts "Building part2: CPU"
+ end
+
+ def build_part3
+ puts "Building part3: display"
+ end
+end
+
+class TableBuilder < ProductBuilder
+ def build_part1
+ puts "Building part1: legs"
+ end
+
+ def build_part2
+ puts "Building part2: top"
+ end
+
+ def build_part3
+ puts "Building part3: mounting"
+ end
+end
+
+# 3. director
+
+class Director
+ def construct(builder)
+ builder.build_part1
+ builder.build_part2
+ builder.build_part3
+ end
+end
+
+# 4. test
+
+director = Director.new
+
+director.construct(ComputerBuilder.new)
+director.construct(TableBuilder.new)
+
77 creational/builder2.rb
View
@@ -0,0 +1,77 @@
+# builder2.rb
+
+# This example uses ruby "method_missing" to implement so-called "magic method"
+
+# 1. type builder interface
+
+class ProductBuilder
+ def build_part1
+ end
+
+ def build_part2
+ end
+
+ def build_part3
+ end
+
+ def method_missing(name, *args)
+ words = name.to_s.split("_")
+
+ return super(name, *args) unless words.shift == 'build'
+
+ words.each do |word|
+ next if word == 'and'
+
+ build_part1 if word == 'part1'
+ build_part2 if word == 'part2'
+ build_part3 if word == 'part3'
+ end
+ end
+
+end
+
+# 2. type builder implementations
+
+class ComputerBuilder < ProductBuilder
+ def build_part1
+ puts "Building part1: motherboard"
+ end
+
+ def build_part2
+ puts "Building part2: CPU"
+ end
+
+ def build_part3
+ puts "Building part3: display"
+ end
+end
+
+class TableBuilder < ProductBuilder
+ def build_part1
+ puts "Building part1: legs"
+ end
+
+ def build_part2
+ puts "Building part2: top"
+ end
+
+ def build_part3
+ puts "Building part3: mounting"
+ end
+end
+
+# 3. director
+
+class Director
+ def construct_with_magic(builder)
+ builder.build_part1_and_part2_and_part3
+ end
+end
+
+# 4. test
+
+director = Director.new
+
+puts "with magic:"
+director.construct_with_magic(ComputerBuilder.new)
+director.construct_with_magic(TableBuilder.new)
27 creational/singleton1.rb
View
@@ -0,0 +1,27 @@
+# singleton1.rb
+
+# Ensure a class only has one instance and provide a global point of access to it.
+
+class MySingleton
+ @@instance = nil
+
+ def self.instance()
+ if @@instance == nil
+ public_class_method :new # enables instance creation for this class
+
+ @@instance = new() unless @@instance
+
+ private_class_method :new # disable instance creation for this class
+ end
+
+ @@instance
+ end
+
+ private_class_method :new # disable instance creation for this class
+end
+
+
+# MySingleton1.new # error
+
+puts "singleton: " + MySingleton.instance.to_s
+puts "singleton: " + MySingleton.instance.to_s
12 creational/singleton2.rb
View
@@ -0,0 +1,12 @@
+# singleton2.rb
+
+# This example does the same qith the help of 'singleton' library.
+
+require 'singleton'
+
+class MySingleton
+ include Singleton
+end
+
+puts "singleton: " + MySingleton.instance.to_s
+puts "singleton: " + MySingleton.instance.to_s
64 creational/singleton3.rb
View
@@ -0,0 +1,64 @@
+# singleton3.rb
+
+# Usually you have to avoid using Singeton, because it's not possible to mock (and consequently test) it.
+#
+# This example is trying to replace Singleton pattern with interface inheritance and dependency injection.
+# Now we can mock the singleton.
+
+# 1. Define singleton.
+
+class Singleton
+ def operation
+ end
+end
+
+# 2. Singleton implementation.
+
+class MySingleton < Singleton
+ def operation
+ puts "operation"
+ end
+end
+
+# 3. Class that uses the singleton object (we should be able to set it up from outside).
+
+class SingletonUser
+ def initialize(singleton)
+ @singleton = singleton
+ end
+
+ def do_something
+ @singleton.operation
+ end
+
+end
+
+# 4. Create mock object for the singleton.
+
+class MockSingleton < Singleton
+
+ def operation
+ puts "mock operation"
+ end
+
+end
+
+
+# 5. test
+
+class SingletonTest
+ def test_singleton
+ singleton = MockSingleton.new
+
+ singleton_user = SingletonUser.new(singleton)
+
+ singleton_user.do_something
+
+ # assertions
+ end
+end
+
+tester = SingletonTest.new
+
+tester.test_singleton
+
161 enterprise/dsl1.rb
View
@@ -0,0 +1,161 @@
+# dsl1.rb
+
+# Domain Specific Languange (DSL) is simple set of commands for serving a specific domain.
+# It could be implemented on object or class level. With object level we've got DSL scripts, with
+# the class level - metaprogramming statements
+
+# This implementation uses object level.
+
+# 1. Defines DSL elements: leaf, branch, root, trunk, tree.
+# If one element appears inside another, use collection and don't forget to eval
+# incoming block for the parent.
+
+module HasName
+ attr_reader :name
+
+ def initialize(name = '')
+ @name = name
+ end
+
+ def to_s
+ "[name: #{name}]"
+ end
+end
+
+class Leaf
+ include HasName
+
+ def to_s
+ "Leaf[name: #{name}]"
+ end
+end
+
+class Branch
+ include HasName
+
+ attr_reader :leaves
+
+ def initialize name
+ super
+
+ @leaves = []
+ end
+
+ def leaf(name, &block)
+ leaf = Leaf.new(name)
+ leaf.instance_eval(&block) if block_given?
+
+ @leaves << leaf
+
+ leaf
+ end
+
+ def to_s
+ "Branch[name: #{name}; leaves: #{leaves.join(', ')}]"
+ end
+end
+
+class Root
+ include HasName
+
+ def to_s
+ "Root[name: #{name}]"
+ end
+end
+
+class Trunk
+ include HasName
+
+ attr_reader :branches, :roots
+
+ def initialize
+ super
+
+ @branches = []
+ @roots = []
+ end
+
+ def branch(name, &block)
+ branch = Branch.new(name)
+ branch.instance_eval(&block) if block_given?
+
+ @branches << branch
+
+ branch
+ end
+
+ def root(name, &block)
+ root = Root.new(name)
+ root.instance_eval(&block) if block_given?
+
+ @roots << root
+
+ root
+ end
+
+ def to_s
+ " Trunk[\n" +
+ " branches: #{branches.join(', ')};\n" +
+ " roots: #{roots.join(', ')};\n" +
+ " ]"
+ end
+end
+
+class Tree
+ include HasName
+
+ def initialize name
+ super
+ @trunk = Trunk.new
+ end
+
+ def type(type)
+ @type = type
+ end
+
+ def trunk(&block)
+ @trunk.instance_eval(&block)
+ end
+
+ def to_s
+ "Tree [\n name: #{name};\n type: #{@type};\n trunk: #{@trunk}\n]"
+ end
+end
+
+# 2. Defines DSL for describing the tree. It acts as the tree builder.
+
+module Tree::DSL
+ def tree(name, &block)
+ tree = Tree.new(name)
+ tree.instance_eval(&block)
+
+ return tree
+ end
+end
+
+# 3. test
+
+include Tree::DSL
+
+# tree description
+
+tree :my_favorite_tree do
+ type :oak
+
+ trunk do
+ branch :b1 do
+ leaf :l1
+ leaf :l2
+ end
+
+ branch :b2 do
+ leaf :l3
+ end
+
+ root :r1
+ root :r2
+ end
+
+ puts self # prints the tree
+end
+
33 enterprise/dsl2.rb
View
@@ -0,0 +1,33 @@
+# dsl2.rb
+
+# This implementation uses class level.
+
+class Object
+ def self.property_creator(*names)
+ names.each do |name|
+ class_eval <<-CODE
+ def #{name}
+ @#{name}
+ end
+
+ def #{name}=(val)
+ @#{name} = val
+ end
+ CODE
+ end
+ end
+end
+
+class Bean
+ property_creator :prop1, :prop2
+
+ def initialize(prop1, prop2)
+ @prop1 = prop1
+ @prop2 = prop2
+ end
+end
+
+bean = Bean.new('red', 'morning')
+
+puts bean.prop1
+puts bean.prop2
5 notes.txt
View
@@ -0,0 +1,5 @@
+Relations:
+
+1. Decorator is Adapter with additional pre-, post- and around- code;
+
+2. Factory Method is Template Method but applied to object creation instead of class creation.
29 structural/adapter.rb
View
@@ -22,7 +22,7 @@ def process(item)
end
end
-# 2. class that creates conflict (adaptee): we cannot pass type in client's process() method.
+# 2. class that creates conflict (adaptee): we cannot pass it in client's process() method.
class OtherItem # adaptee
def other_operation
@@ -32,7 +32,7 @@ def other_operation
# 3. adaptation
-class ClientItemAdapter < ClientItem # adapter
+class OtherItemAdapter < ClientItem # adapter
def initialize
@other_item = OtherItem.new
end
@@ -51,15 +51,34 @@ def some_operation
# client.process(OtherItem.new) # OtherItem does not conform CientItem protocol
-client.process(ClientItemAdapter.new) # OtherItem is adapted to CientItem protocol through adapter
+client.process(OtherItemAdapter.new) # OtherItem is adapted to CientItem protocol through adapter
+
+# here we adapt incompatible interface "on the fly" using ruby ability to dynamically
+# add missing method to the class.
other_item = OtherItem.new
class << other_item
def some_operation
- puts "some operation for this instance"
+ puts "some operation for this instance (v1)"
end
end
-client.process(other_item) # OtherItem is adapted to CientItem protocol with the help of odifying instance behavior
+client.process(other_item)
+
+other_item2 = OtherItem.new
+
+def other_item2.some_operation
+ puts "some operation for this instance (v2)"
+end
+
+client.process(other_item2)
+
+# or adding this method directly to the class by openning it:
+class OtherItem
+ def some_operation
+ puts "some operation for this instance (v3)"
+ end
+end
+client.process(OtherItem.new)
91 structural/decorator1.rb
View
@@ -0,0 +1,91 @@
+# decorator1.rb
+
+# Attaches additional responsibilities to an object dynamically.
+# Provides a flexible alternative to subclassing for extending functionality.
+
+# 1. type interface
+
+class Component
+ def operation
+ end
+end
+
+# 2. type implementation
+
+class ConcreteComponent < Component
+ def operation
+ puts "my component"
+ end
+end
+
+# 3a. traditional solution based on inheritance
+
+class TraditionalComponent < ConcreteComponent
+ def pre_decoration
+ puts "my component pre decoration"
+ end
+
+ def post_decoration
+ puts "my component post decoration"
+ end
+
+ def operation
+ pre_decoration
+
+ super
+
+ post_decoration
+ end
+end
+
+# 3b. solution based on decorator
+
+class Decorator < Component
+ def initialize(component)
+ @component = component
+ end
+
+ def pre_decoration
+ puts "my component pre decoration"
+ end
+
+ def post_decoration
+ puts "my component post decoration"
+ end
+
+ def operation
+ pre_decoration
+
+ @component.operation
+
+ post_decoration
+ end
+end
+
+
+# 4. test
+
+component1 = TraditionalComponent.new
+component1.operation
+puts '-------'
+
+component2 = Decorator.new(ConcreteComponent.new)
+component2.operation
+puts '-------'
+
+component4 = ConcreteComponent.new
+
+class << component4
+ alias old_operation operation
+
+ def operation
+ puts "my component pre decoration"
+
+ old_operation
+
+ puts "my component post decoration"
+ end
+end
+
+component4.operation
+puts '-------'
39 structural/decorator2.rb
View
@@ -0,0 +1,39 @@
+# decorator2.rb
+
+# Attaches additional responsibilities to an object dynamically.
+# Provides a flexible alternative to subclassing for extending functionality.
+
+# 1. type interface
+
+class Component
+ def operation
+ end
+end
+
+# 2. type implementation
+
+class ConcreteComponent < Component
+ def operation
+ puts "my component"
+ end
+end
+
+# 3. This solution uses 'forwardable' library
+
+require 'forwardable'
+
+class ForwardableDecorator
+ extend Forwardable
+
+ def_delegators :@component, :operation
+
+ def initialize(component)
+ @component = component
+ end
+end
+
+# 4. test
+
+component = ForwardableDecorator.new(ConcreteComponent.new)
+component.operation
+puts '-------'
38 structural/decorator3.rb
View
@@ -0,0 +1,38 @@
+# decorator3.rb
+
+# This example changes original method behavior for the instance with the help of ruby "alias".
+
+# 1. type interface
+
+class Component
+ def operation
+ end
+end
+
+# 2. type implementation
+
+class ConcreteComponent < Component
+ def operation
+ puts "my component"
+ end
+end
+
+
+# 3. test
+
+component = ConcreteComponent.new
+
+class << component
+ alias old_operation operation
+
+ def operation
+ puts "my component pre decoration"
+
+ old_operation
+
+ puts "my component post decoration"
+ end
+end
+
+component.operation
+puts '-------'
40 structural/proxy1.rb
View
@@ -0,0 +1,40 @@
+# proxy1.rb
+
+# Provides a surrogate or placeholder for another object to control access to it.
+
+# 1. type interface
+
+class Subject
+ def process
+ end
+end
+
+# 2. type implementation
+
+class MySubject < Subject
+ def process
+ puts "my process"
+ end
+end
+
+# 3. type proxy implementation
+
+class SubjectProxy < Subject
+
+ def initialize(subject)
+ @subject = subject
+ end
+
+ def process
+ puts("Delegating 'process' message to subject.")
+
+ @subject.process
+ end
+end
+
+
+# 4. test
+
+proxy = SubjectProxy.new(MySubject.new)
+
+proxy.process
40 structural/proxy2.rb
View
@@ -0,0 +1,40 @@
+# proxy2.rb
+
+# Provides proxy implementation with ruby's method_missing.
+
+# 1. type interface
+
+class Subject
+ def process
+ end
+end
+
+# 2. type implementation
+
+class MySubject < Subject
+ def process
+ puts "my process"
+ end
+end
+
+# 3. type proxy implementation
+
+class DynamicSubjectProxy
+
+ def initialize(subject)
+ @subject = subject
+ end
+
+ def method_missing(name, *args)
+ puts("Delegating '#{name}' message to subject.")
+
+ @subject.send(name, *args)
+ end
+end
+
+
+# 4. test
+
+dynamic_proxy = DynamicSubjectProxy.new(MySubject.new)
+
+dynamic_proxy.process
Please sign in to comment.
Something went wrong with that request. Please try again.