Structural
design patterns describe how objects and classes can be combined and form a
large structure and that ease design by identifying a simple way to realize
relationships between entities.
There are
seven structural patterns,
Adapter, Bridge,
Composite, Decorator, Facade, Flyweight & Proxy
I.
Adapter pattern: - This design pattern is used to
allow two incompatible types to communicate. Where one class relies upon a
specific interface that is not implemented by another class, the adapter acts
as a translator between the two types. Pattern will define relationship between
objects.
“This pattern involves a single class called
adapter which is responsible for communication between two independent or
incompatible interfaces”
It’s
usually implemented when requirements changed and we must implement some
functionality of classes which interfaces are not compatible with ours.
For more
understanding please check below sample code snippet; I have added comments
inline for better understanding
public interface ITarget//This is an interface
which is used by the client to achieve its functionality/request.
{
void TestMethodA();
}
public class Adapter : Adaptee, ITarget//This is a class which implements the
ITarget interface and inherits the Adaptee class. It is used for communication
between Client and Adaptee.
{
public void TestMethodA()
{
TestMethodB();
}
}
public class Adaptee//Adaptee
class have the functionality that
is required by the client. However, its
interface is not compatible with the client.
{
public void TestMethodB()
{
Console.WriteLine("TestMethodB()
is called");
}
}
II.
Bridge Design Pattern: The bridge pattern is a design
pattern that separates the abstract elements of a class from its technical
implementation. This provides a cleaner implementation of real-world objects
and allows the implementation details to be changed easily. So basically Bridge
pattern helps to decouple abstraction from implementation.
Understanding:
-For sake of explanation consider a switch which can be used to on/off Bulb,
TV, and Refrigerator etc. Here switch is
the abstraction and the electronic equipment’s are the implementations. The
switch can be applied to any electronic equipment, so the switch is an abstract
thinking while the equipment’s are implementations.
For
detail understanding in terms of coding refer below code snippet,
//Interface
for implantation
public interface IEquipment
{
void Start();
void Stop();
}
//Equipment we have
class Bulb :IEquipment
{
public void Start()
{
Console.WriteLine("Switch On
Bulb");
}
public void Stop()
{
Console.WriteLine("Switch Off
Bulb");
}
}
class TV : IEquipment
{
public void Start()
{
Console.WriteLine("Switch On
TV");
}
public void Stop()
{
Console.WriteLine("Switch Off
TV");
}
}
//Redefined
Abstraction Interface
interface ISwitch
{
void On();
void Off();
}
//Bridge
class Switch:ISwitch
{
private IEquipment objEquipment;// Aggregate equipment
object reference
public void setEquipment(IEquipment Equipment)
{
objEquipment = Equipment;
}
//Interface Implementation
public void On()
{
objEquipment.Start();
}
public void Off()
{
objEquipment.Stop();
}
}
static void Main(string[] args)
{
//Create Implementation Object
Bulb objBulb = new Bulb();
TV objTV = new TV();
//Abstarct Object
Switch objSwitch = new Switch();
//Here as you can see implmentation is
separate from abstaraction
objSwitch.setEquipment(objBulb);
objSwitch.On();
objSwitch.Off();
objSwitch.setEquipment(objTV);
objSwitch.On();
objSwitch.Off();
}
I know its
bit complex to understand, I have added comments inline for better understanding.
So with this code now if the implementation changes it does not affect
abstraction and vice versa.
III.
Composite Design Pattern: - “A tree structure of simple and
composite objects”-GOF
This
design pattern is used when creating hierarchical object models. The pattern
defines a manner in which to design recursive tree structures of objects, where
individual objects and groups can be accessed in the same manner. When need to manipulate
a tree structure of objects, Composite pattern can be used.
So
for implementation what we have is one Composite,
abstract class which will define behaviors and all classes to use common
interface. We will treat the main and leaf object uniformly.
IV.
Decorator: - The decorator pattern is a
design pattern that extends the functionality of individual objects by wrapping
them with one or more decorator classes. These decorators can modify existing
members and add new methods and properties at run-time.
Basically
it is a attaching additional responsibilities to an object dynamically.
Extending
object’s functionality can be done statically /compile time by using inheritance
or dynamically/run time by wrapping them in an object of a decorator class.
Decorator pattern is applied on individual objects, not classes. And it’s an
alternative to subclassing. This pattern allows creating a multiple decorators
that can be stacked on the top of each other, each time adding a new
functionality to the overridden method.
V.
Facade: -Facade defines a higher-level
interface that makes the subsystem easier to use. It provides a unified
interface to a set of interfaces in a subsystem.
Facade
pattern makes software library easier to use, understand, test and can reduces
dependencies. We can say that facade pattern is some kind of wrapper that
contains a set of members so they are easy to understand and use.
For
more understanding, please refer below code,
In
given example we have,
Facade Class-MortgageApplication
·
Knows
which subsystem classes are responsible for a request.
·
Delegate
client requests to appropriate subsystem objects.
Subsystem classes- Bank, Credit, Loan
·
Implement
subsystem functionality.
·
Handle
work assigned by the Facade object.
·
Have
no knowledge of the facade and keep no reference to it.
In given
example, we are checking if customer is eligible for Loan, so we are creating object of Mortgage class
(Facade) and calling to IsEligible method, which will call subsystem methods, HasSufficientSavings,
HasNoBadLoans, HasGoodCredit to evaluate customer.
/// <summary>
/// Facade Design Pattern.
/// </summary>
class MainApp
{
/// <summary>
/// Entry point
/// </summary>
static void Main()
{
// Facade
Mortgage mortgage = new Mortgage();
// Evaluate mortgage eligibility for
customer
Customer customer = new Customer("Mangesh
G");
bool eligible = mortgage.IsEligible(customer,
125000);
Console.WriteLine("\n" + customer.Name +
" has been
" +
(eligible ? "Approved" : "Rejected"));
// Wait for user
Console.ReadKey();
}
}
/// <summary>
/// Subsystem class
/// </summary>
class Bank
{
public bool HasSufficientSavings(Customer c, int amount)
{
Console.WriteLine("Check bank for
" +
c.Name);
return true;
}
}
/// <summary>
/// Subsystem class
/// </summary>
class Credit
{
public bool HasGoodCredit(Customer c)
{
Console.WriteLine("Check credit for " + c.Name);
return true;
}
}
/// <summary>
/// Subsystem class
/// </summary>
class Loan
{
public bool HasNoBadLoans(Customer c)
{
Console.WriteLine("Check loans
for "
+ c.Name);
return true;
}
}
/// <summary>
/// Customer class
/// </summary>
class Customer
{
private string _name;
// Constructor
public Customer(string name)
{
this._name = name;
}
// Gets the name
public string Name
{
get { return _name; }
}
}
/// <summary>
// Facade Class
/// </summary>
class Mortgage
{
private Bank _bank = new Bank();
private Loan _loan = new Loan();
private Credit _credit = new Credit();
public bool IsEligible(Customer cust, int amount)
{
Console.WriteLine("{0} applies
for {1:C} loan\n",
cust.Name, amount);
bool eligible = true;
// Check creditworthyness of applicant
if (!_bank.HasSufficientSavings(cust, amount))
{
eligible = false;
}
else if (!_loan.HasNoBadLoans(cust))
{
eligible = false;
}
else if (!_credit.HasGoodCredit(cust))
{
eligible = false;
}
return eligible;
}
}
VI.
Flyweight: -The flyweight pattern is a design
pattern that is used to minimize resource usage when working with very large
numbers of objects. This pattern is rarely used, so not going into detail.
VII.
Proxy: - Proxy patterns provide a placeholder
for another object to control access to it.
Proxy
fundamentally is a class functioning as in interface which points towards the
actual class which has data. Proxy can be used when we don’t want to access to
the resource or subject directly because of some base of permissions or if we
don’t want to expose all of the methods of subject class. This data can be a
huge image or a very large object and cannot be duplicated. So multiple proxies
can be created and pointed towards the huge memory consuming object and perform
operations. This avoids duplication of the object and thus saving memory.
For
more understanding, please refer below code,
In
given example we have,
Proxy-ImageProxy Class
·
Maintains
a reference that lets the proxy access the real subject. Proxy may refer to a
Subject if the RealSubject and Subject interfaces are the same.
·
Provides
an interface identical to Subject's so that a proxy can be substituted for the
real subject.
·
Controls
access to the real subject and may be responsible for creating and deleting it.
·
Other
responsibilities depend on the kind of proxy:
o
Remote
proxies are responsible for encoding a request and its arguments and for
sending the encoded request to the real subject in a different address space.
o
Virtual
proxies may cache additional information about the real subject so that they
can postpone accessing it. For example, the ImageProxy from the Motivation
caches the real images's extent.
o
Protection
proxies check that the caller has the access permissions required to perform a
request.
Subject-IImage Interface
·
Defines
the common interface for RealSubject and Proxy so that a Proxy can be used
anywhere a RealSubject is expected.
RealSubject-Image Class
·
Defines
the real object that the proxy represents.
/// <summary>
/// Proxy Design Pattern Example
/// </summary>
class MainApp
{
/// <summary>
/// Entry point
/// </summary>
static void Main()
{
// Create proxy
ImageProxy proxy = new ImageProxy();
proxy.LoadImage();
proxy.ImageDiscription();
}
}
/// <summary>
/// The 'Subject interface
/// </summary>
public interface IImage
{
void LoadImage();
string ImageDiscription();
}
/// <summary>
/// The 'RealSubject' class
/// </summary>
class Actualmage : IImage
{
public void LoadImage()
{
Console.WriteLine("Loaidng
Image...");
}
public string ImageDiscription()
{
Console.WriteLine("Loading image
discription form file...");
return "Image discription";
}
}
/// <summary>
/// The 'Proxy Object' class
/// </summary>
class ImageProxy : IImage
{
private Actualmage _Image = new Actualmage();
public void LoadImage()
{
_Image.LoadImage();
}
public string ImageDiscription()
{
return _Image.ImageDiscription();
}
}
Comments
Post a Comment