Deal with object creation mechanisms
Trying to create objects in a manner suitable to the situation
Composed of two dominant ideas
Encapsulating knowledge about which concrete classes the system uses
Hiding how instances of these concrete classes are created and combined
Class that is guaranteed to have only a single instance and provides global point of access to it
Examples: window manager, file system, console, application logger, state object
Encapsulated "just-in-time initialization" or "initialization on first use".
public Logger
{
private static Logger instance;
// private constructor ensures that
// Logger cannot be instantiated from outside
private Logger()
{ }
// provide access to the single instance
public static Logger Instance
{
get
{
// instantiate on the first access
if(instance == null)
instance = new Logger();
return instance;
}
}
public void Log(string log)
{
Console.WriteLine(log);
}
}
The Singleton design pattern is one of the most inappropriately used patterns
Designers frequently use Singletons in a misguided attempt to replace global variables
Singletons can make testing harder because they often rely on static methods and properties
Defines an abstract interface for creating a family of related types of objects
The created objects are returned as interface types or base types
Multiple factories can implement the abstract interface
If an application is to be portable, it needs to encapsulate platform dependencies
Too often, this encapsulation is not engineered in advance, and lots of #ifdef case statements with options for all currently supported platforms begin to procreate like rabbits throughout the code
interface IContinentFactory { // AbstractFactory
Herbivore CreateHerbivore();
Carnivore CreateCarnivore();
}
class AfricaFactory : IContinentFactory {
public Herbivore CreateHerbivore() {
return new Wildbeаst();
}
public Carnivore CreateCarnivore() {
return new Lion(); // Constructor can be internal
}
}
class AmericaFactory : IContinentFactory {
public Herbivore CreateHerbivore() {
return new Bison();
}
public Carnivore CreateCarnivore() {
return new Wolf();
}
}
This pattern can introduce a lot of accidental complexity
One of the main drawbacks is the extra complexity and writing the code during the initial stages
Does your application really need an Abstract Factory?
public abstract class AbstractFactory {
abstract Color GetColor(String color);
abstract Shape GetShape(String shape) ;
}
public class ShapeFactory: AbstractFactory {
private override Shape GetShape(String shapeType){
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
}
return null;
}
private override Color GetColor(String color) {
return null;
}
}
Describe ways to assemble objects to implement a new functionality
Simple way to realize relationships between entities
All about Class and Object composition
Structural class-creation patterns use inheritance to compose interfaces
Structural object-patterns define ways to compose objects to obtain new functionality
We have different objects and we want to treat them the same way
We want to present hierarchy of objects
Tree-like structures
Examples in .NET Framework
Windows.Forms.Control and its derived classes
System.Web.UI.Control and its derived classes
System.Xml.XmlNode and its derived classes
abstract class MailReceiver
{
public abstract void SendMail();
}
class EmailAddress : MailReceiver
{
public override void SendMail() { /*...*/ }
}
class GroupOfEmailAddresses : MailReceiver
{
private List<MailReceiver> participants;
public override void SendMail()
{
foreach(var p in participants)
{
p.SendMail();
}
}
}
static void Main()
{
var rootGroup = new GroupOfEmailAddresses();
rootGroup.SendMail();
}
Alternative to inheritance (class explosion)
Support Open-Closed principle
Flexible design, original object is unaware
You want to add behavior or state to individual objects at run-time
Inheritance is not feasible because it is static and applies to an entire class
This flexibility can be achieved with the following design
abstract class Decorator : LibraryItem {
protected LibraryItem libraryItem;
public Decorator(LibraryItem libraryItem) {
this.libraryItem = libraryItem;
}
public override void Display() {
libraryItem.Display();
}
}
class Borrowable : Decorator {
protected List<string> borrowers = new List<string>();
public Borrowable(LibraryItem libraryItem) : base(libraryItem) {}
public void BorrowItem(string name) {
borrowers.Add(name);
libraryItem.NumCopies--;
}
public override void Display() {
base.Display();
foreach (string borrower in borrowers) {
Console.WriteLine(" borrower: " + borrower);
}
}
}
Converts the given class' interface into another class requested by the client
Wraps an existing class with a new interface
Impedance match an old component to a new system
Allows classes to work together when this is impossible due to incompatible interfaces
An "off the shelf" component offers compelling functionality that you would like to reuse
But its "view of the world" is not compatible with the philosophy and architecture of the system currently being developed
interface ITarget {
List<string> GetProducts();
}
public class VendorAdaptee
{
public List<string> GetListOfProducts() {
List<string> products = new List<string>();
products.Add("Gaming Consoles");
products.Add("Television");
products.Add("Books");
products.Add("Musical Instruments");
return products;
}
}
class VendorAdapter:ITarget {
public List<string> GetProducts() {
VendorAdaptee adaptee = new VendorAdaptee();
return adaptee.GetListOfProducts();
}
}
class ShoppingPortalClient {
static void Main(string[] args) {
ITarget adapter = new VendorAdapter();
foreach (string product in adapter.GetProducts()) {
Console.WriteLine(product);
}
Console.ReadLine();
}
}
Composite pattern handles a single object and group of objects differently?
Decorator pattern works in favour of keeping the Open-Closed principle?
If you have an old class with API that does not work anymore which pattern are you going to use?
Differences between Decorator pattern and inheritance?
Adapter pattern creates new implementations of the objects of old system?
The 'Adaptee' class is the one which interface is not compatible or the class that takes care of the implementing the new interface?
The 'Target' class is?
8. Which design pattern is illustrated with:
Concerned with communication (interaction) between the objects
Either with the assignment of responsibilities between objects
Or encapsulating behavior in an object and delegating requests to it
Increase flexibility in carrying out cross-classes communication
Defines the base of an algorithm in a method, leaving some implementation to its subclasses
Allows the subclasses to redefine the implementation of some of the parts of the algorithm
Doesn’t let the subclasses to change the algorithm structure
Relies on inheritance
Strategy on composition
Usually, override of virtual or abstract method (hook)
Two or more classes should follow the same common algorithm or workflow
The workflow never changes
Subclasses may redefine the steps (not order)
Some steps may be implemented in the base class (DRY)
public abstract class HotDrink {
public void PrepareRecipe()
{
BoilWater(); Brew(); PourInCup(); AddSpices();
}
protected abstract void Brew();
protected abstract void AddSpices();
private void BoilWater() { ... }
private void PourInCup() { ... }
}
public class Coffee : HotDrink {
protected override void Brew() { ... }
protected override void AddSpices() { ... }
}
public class Tea : HotDrink {
protected override void Brew() { ... }
protected override void AddSpices() { ... }
}