Tuesday, 2 April 2024

Proxy pattern

The proxy pattern provides a surrogate or placeholder for another object in order to control access to it. In essence what that means is that the proxy acts as a middle man between the target and the source,  this may sound familiar; it is more or less exactly what the facade, adapter, and decorator patterns do. The difference however is in the intent or reason behind these patterns, in the case of the proxy pattern we have several different types of proxies:

Remote proxy: A remote proxy acts as a local representative for an object located in a different address space, such as on a different machine or server. It facilitates communication and data transfer between the client and the remote object. It abstracts the details of network communication, allowing the client to interact with a remote object seamlessly.

Virtual proxy: A virtual proxy acts as a placeholder for an object that is expensive to create or load. It delays the instantiation or loading of the actual object until it is genuinely needed. Provides a “stand-in” for an actual object, which is instantiated only on demand.

Protection proxy: A protection proxy controls access to an object by implementing additional security, permissions, or authentication mechanisms. It determines whether the client is authorised to interact with the object. It acts as a gatekeeper, allowing or denying access based on specific rules.

Logging proxy: Records details about the interactions with the real object, often for debugging, monitoring, or analytics purposes.

Caching proxy: A caching proxy stores frequently accessed data or responses to improve performance and reduce resource usage.


To name a few, despite the difference in intent they accomplish their goal in the same way, the key concept to a proxy is that it eavesdrops on a call. Let's hypothetically say that we have the following configuration.



Above we depict a contrived, but fairly common configuration, we have an object which we create locally and a rest service to load and save that object to and from some sort of cloud repository. Now as before let's imagine that it is a significantly more intricate, and let's also say that perhaps these objects are significantly more intricate and take up much more memory, but don't change very much. To solve a problem such as that a caching proxy would make perfect sense. Our UML would look like the following.


Exactly like the decorator pattern, the proxy has this "is a" and "has a" relationsip, however unlike the decorator pattern the intent of a proxy is not to add or change functionality, but to intercept, do something, and then pass the original message back to the caller. 

In code it could like like the following without the proxy



interface IIngredient {
id: string;
name: string;
unit: number;
pricePerUnit: number;
}

class Ingredient implements IIngredient {
id: string;
name: string;
unit: number;
pricePerUnit: number;

constructor(id: string, name: string, unit: number, pricePerUnit: number) {
this.id = id;
this.name = name;
this.unit = unit;
this.pricePerUnit = pricePerUnit;
}

getPrice(): number {
return this.unit * this.pricePerUnit;
}

getDescription(): string {
const quantity = this.unit;
const parts = `${quantity < 1 ? 'part' : 'parts'}`;
const name = this.name;

return `${quantity} ${parts} ${name}`;
}
}

interface IIngredientService {
url: string;

getIngredient(id:string): Promise<Ingredient[]>;
createIngredient(ingredient: Ingredient): Promise<Ingredient>;
updateIngredient(ingredient: Ingredient): Promise<Ingredient>;
}

class IngredientService implements IIngredientService {
url: string;

constructor(url: string) {
this.url = url;
}

async getIngredient(id: string): Promise<Ingredient[]> {
const response = await fetch(`${this.url}/ingredients/${id}`);
const data = await response.json();
return data.map((item: any) => new Ingredient(item.id, item.name, item.unit, item.pricePerUnit));
}

async createIngredient(ingredient: Ingredient): Promise<Ingredient> {
const response = await fetch(`${this.url}/ingredients`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(ingredient)
});
const data = await response.json();
return new Ingredient(data.id, data.name, data.unit, data.pricePerUnit);
}

async updateIngredient(ingredient: Ingredient): Promise<Ingredient> {
const response = await fetch(`${this.url}/ingredients/${ingredient.id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(ingredient)
});
const data = await response.json();
return new Ingredient(data.id, data.name, data.unit, data.pricePerUnit);
}
}


and then if we add the proxy it could like like the following


class ProxyCache implements IIngredientService {
url: string;
private cache: Map<string, Ingredient[]> = new Map();
private ingredientService: IngredientService;

constructor(url: string) {
this.url = url;
this.ingredientService = new IngredientService(url);
}

async getIngredient(id: string): Promise<Ingredient[]> {
if (this.cache.has(id)) {
return this.cache.get(id)!;
} else {
const ingredients = await this.ingredientService.getIngredient(id);
this.cache.set(id, ingredients);
return ingredients;
}
}

async createIngredient(ingredient: Ingredient): Promise<Ingredient> {
const newIngredient = await this.ingredientService.createIngredient(ingredient);
this.cache.set(newIngredient.id, [newIngredient]);
return newIngredient;
}

async updateIngredient(ingredient: Ingredient): Promise<Ingredient> {
const updatedIngredient = await this.ingredientService.updateIngredient(ingredient);
this.cache.set(updatedIngredient.id, [updatedIngredient]);
return updatedIngredient;
}

async savetoCache(ingredient: IIngredient): Promise<void> {
this.cache.set(ingredient.id, [ingredient as Ingredient]);
}

async loadFromCache(id: string): Promise<IIngredient[]> {
if (this.cache.has(id)) {
return this.cache.get(id)!;
} else {
throw new Error('Ingredient not found in cache');
}
}
}


notice that our proxy class, makes no changes to the request, it simply stores it in a cache, then on a subsequent call it checks if the request is in the cache before making a network call.

Thursday, 28 March 2024

Decorator pattern

The Decorator Pattern is a design pattern used to dynamically add new functionality at runtime to an object, without altering their structure. This ability to modify an objects behaviour dynamically is the advantage over using class inheritance. When this need to dynamically alter an object's functionality does not exist than the decorator pattern is most likely over engineering a solution. 

It is most appropriate for situations in which either you find yourself creating dozens of classes that are extremely similar, but have slightly variation functionality (known as class explosion), or in situations where their is a high likelihood that you will need to append existing functionality in the future while maintaining its original capabilities.

You can think of decorators as wrappers, for a concrete class, they can add functionality or modify existing behaviour. 

In the above illustration, you can start to visualise how the decorator works, it wraps a concrete class and either modifies functionality or adds functionality, in theory you could have an unlimited number of wrappers. Below is a UML representation of how this works


Notice that the IDecorator interface, not only implements or extends either the base interface or concrete class, but it also has a reference to to that base definition or implementation. This creates both a "Has a" and "Is A" relationship with the base concrete class. This provides us with two advantages, firstly and most commonly this allows us to dynamically add functionality and behaviour to existing classes at runtime, the most common example of this beverage pricing and naming. 

The most common example used to teach the decorator pattern is a rather contrived coffee house example. The coffeehouse example lends itself very well to illustrate how this pattern works, however it's not really how one would leverage this pattern in production code. Instead of the coffee example, I'm going to create a cocktail one. I realise that it sounds exactly the same, and to a degree it is. The reason I'm going with a cocktail example is because after the we understand the fundamentals of how the decorator pattern works. The cocktail example acts as an excellent jump off point to emphasise a real world problem which this pattern is meant to solve.

Let's start with an ingredient interface and class, each beverage will be made up of a combination of ingredients which will determine the price of the finished drink.


interface IIngredient {
name: string;
pricePerUnit: number;
unit: number
}

class Ingredient implements IIngredient {
name: string;
pricePerUnit: number;
unit: number

constructor(name: string, price: number, unit: number) {
this.name = name;
this.pricePerUnit = pricePerUnit;
this.unit = unit;
}
}

At the very base level, whether we are talking wine, cocktails, mocktails, coffee, or Italian sodas, every beverage is going to have a price, and a recipe, at this point it would make sense to include an array of Ingredients in our Beverage class, however it would completely defeat the need for the decorator pattern, for this reason, though this example articulates the use of the decorator pattern rather well, it's not a practical one. 


abstract class Beverage {
abstract getPrice(): number;
abstract getRecipe(): string;
}

Notice that anything that inherits from the abstract Beverage class is going to have to implement both the getPrice and getRecipe functions. Next let's create a cocktail class which extends the abstract Beverage class and implements the abstract methods getPrice and getRecipe().


export default class Cocktail extends Beverage {
name: string;

constructor(name: string) {
super();
this.name = name;
}
getPrice(): number {
return 0;
}

getRecipe(): string {
return `A ${this.name} made with`;
}
}

With that complete we have a UML diagram like so


notice that our Ingredient interface and class are not really attached to anything, let's now introduce the idea of a decorator and fix that.


abstract class CocktailDecorator extends Beverage {
base: Beverage;
ingredient: IIngredient;

constructor(base: Beverage, name: string, price: number, oz: number) {
this.base = base;
this.ingredient = new Ingredient(name, price, oz);
}

getPrice = (): number => {
const price = this.ingredient.ozPrice;
const quantity = this.ingredient.ozQuantity

const thisPrice = price * quantity;
const basePrice = this.base.getPrice();

return basePrice + thisPrice;
}

getRecipe = (): string => {
const prefix = this.base.getRecipe();
const conjunction = this.base instanceof Cocktail ? ":" : ",";
const quantity = this.ingredient.ozQuantity;
const parts = `${quantity < 1 ? 'part' : 'parts'}`
const name = this.ingredient.name;

return `${prefix}${conjunction} ${quantity} ${parts} ${name}`;
};
}

Here we have this interesting "is a" and "has a" relationship, this is what allows us to wrap not just anything that inherits from the abstract Beverage class, but it also lets us modify the functionality of anything defined in the Beverage class. notice that our decorator uses the ingredient to calculate the price as well as the recipe.

Lets create three variations of our decorator


export class VodkaDecorator extends CocktailDecorator {
constructor(base: Beverage, oz: number) {
super(base, "Vodka", 4, oz);
}
}

export class GinDecorator extends CocktailDecorator {
constructor(base: Beverage, oz: number) {
super(base, "Gin", 5, oz);
}
}

export class VermouthDecorator extends CocktailDecorator {
constructor(base: Beverage, oz: number) {
super(base, "Vermouth", 1, oz);
}
}

Now we can instantiate a either a gin or vodka martini like so


const vodkaMartini = new VermouthDecorator(new VodkaDecorator(new Cocktail("Vodka martini"), 2), .25);
const ginMartini = new VermouthDecorator(new GinDecorator(new Cocktail("Gin martini"), 2), .25);

This demonstrates how we can use the decorator pattern to append functionality to an existing base implementation, notice that in this case we create this daisy chain of classes which implement and have an instance of the Beverage class, down to the base concrete implementation which only implements the abstract Beverage class. 

One thing to keep in mind is that nesting order matters, when we call 

console.log(vodkaMartini.getRecipe());
console.log(vodkaMartini.getPrice());
console.log(ginMartini.getRecipe());
console.log(ginMartini.getPrice());

Each getRecipe and getPrice function is called from the outer down to the inner wrapper, this is why when we defined the getRecipe class in the base CocktailDecorator we leverage this idea of a prefix, so that the concretes GetRecipe implementation would be at the front of the output.


One thing to keep in mind when using the decorator pattern, any functionality that has been altered by the decorators is called from the outer ring down to centre. 

Our final UML looks something like this:


As mentioned before, this is a contrived example and though it illustrates how the decorator pattern works, it does not provide a realistic example of what context to actually use it in. For a problem like this, it would be far more reasonable for the cocktail class to be instantiated with an array of ingredients with an option to add/remove ingredients; so let's refactor our code to more appropriate version



notice how we've simplified our models 


interface IIngredient {
name: string;
ozPrice: number;
ozQuantity: number;
}

abstract class Priced {
abstract getPrice(): number;
abstract getDescription(): string;
}

export class Ingredient extends Priced implements IIngredient {
name: string;
ozPrice: number;
ozQuantity: number;

constructor(name: string, ozPrice: number, ozQuantity: number) {
super();
this.name = name;
this.ozPrice = ozPrice;
this.ozQuantity = ozQuantity;
}

getPrice(): number {
return this.ozPrice * this.ozQuantity;
}
getDescription(): string {
const quantity = this.ozQuantity;
const parts = `${quantity < 1 ? 'part' : 'parts'}`
const name = this.name;

return `${quantity} ${parts} ${name}`;
}
}

export default class Cocktail extends Priced {
name: string;
ingredients: Ingredient[]

constructor(name: string, ingredients:Ingredient[]) {
super();
this.name = name;
this.ingredients = ingredients;
}
getPrice(): number {
return this.ingredients.reduce((sum, item)=> sum + item.getPrice(), 0);
}

getDescription(): string {
return this.name + ': ' + this.ingredients.reduce((recipe, item)=> recipe + `, ${item.getPrice()}`, "");
}

addIngredient (ingredient: Ingredient) {
const i = this.ingredients.find(i => i.name = ingredient.name);
if(i)
i.ozQuantity += ingredient.ozQuantity;
else
this.ingredients.push(ingredient);
}

removeIngredient(ingredient: Ingredient){
const i = this.ingredients.findIndex(i => i.name = ingredient.name);
if(i > -1) {
const t = this.ingredients[i];
t.ozQuantity -= ingredient.ozQuantity;
if(t.ozQuantity < 0)
this.ingredients.splice(i,1);
}
}
}

we've significantly reduced the complexity of our code, now let's create a menu object; makes sense right, it's reasonable for a predefined menu of cocktails to exist for patrons to choose from with the ability to modify them, swap out the alcohol for a preferred brand, or add a double shot.


class Menu{
cocktails: Cocktail[]
constructor(cocktails: Cocktail[]){
this.cocktails = cocktails;
}

listCocktails(): [string, number][]{
return this.cocktails.map(c=> [c.getDescription(), c.getPrice()])
}
}

Let's say we go live with our solution, and it works as far as we know, we can define cocktails, their ingredients, and generate a menu. Now hypothetically let's say someone orders a dirty martini with 10 extra shots of vodka, well that's fine and dandy, but what about glasses? don't they have a capacity? now our example is rather trivial and i would say forget the open/closed principle and just refactor the code; and in this context no one would disagree with me, however in financial software that has thousands of legacy lines of code, no one would let you make that change for fear of introducing more bugs. 

Now for an intricate situation like that it would make sense to introduce a decorator, this way we can ensure that the original code continues to function, but we can now extend our system without risking legacy code.


export class GlassDecorator extends Cocktail{
base: Cocktail;
maxVolume: number;

constructor(base: Cocktail, maxVolume: number) {
super(base.name, base.ingredients);
this.base = base;
this.maxVolume = maxVolume
}

getFilledVolume(): number{
return this.base.ingredients.reduce((sum, i)=> sum + i.ozQuantity, 0);
}

override addIngredient(ingredient: Ingredient): void {
let filledVolume = this.getFilledVolume();
if(ingredient.ozQuantity + filledVolume > this.maxVolume)
throw Error("the glass does not have enough volume");
this.base.addIngredient(ingredient);
}
}

Our final UML looks like the following.


We can now use our Cocktail class and Glass decorator like so, :

const ginMartini = new Cocktail("gin martini", [gin, vermouth]);
const glass = new GlassDecorator(ginMartini, 5);

glass.addIngredient(new Ingredient("ice", 0, 4))

ginMartini.getDescription();
ginMartini.getPrice();

In conclusion the decorator pattern is useful when you want to add functionality to legacy code, but you want to mitigate any chance of impacting existing code. The change in behaviour impacts from the outside in. meaning that any function that is overridden or appended is first executed on the outer ring of decorators first.


A quick note on UML class diagrams, I have been showing code before the Class diagram. This is not how my actual or anyones work flow should go, software engineers use UML diagrams to plan out their code, to optimally structure it before writing code.

Tuesday, 12 March 2024

Facade pattern

The Facade pattern is a design pattern that provides a simplified, unified interface to a set of interfaces in a subsystem, making it easier to use. It hides the complexities of the subsystem by providing a single point of access, thus reducing dependencies and making the subsystem easier to work with.

If you follow the Single Responsibility Principle and create a vast network of decoupled classes all of which focus on their own specific responsibility, you can very quickly create a rather complex ecosystem of loosely coupled classes, which can prove to be extremely beneficial from the code maintenance perspective. It's much easier to maintain and upgrade a lot of small focused classes, than it is to try and work with one giant monolith of spaghetti code; however this decoupled ecosystem may prove to be more difficult to work with for consumers of the functionality. 

The facade pattern aims to alleviate this complexity by providing a unified interface to a set of interfaces in a subsystem. The facade defines a higher-level interface that makes the subsystem easier to work with.

When should you use the Facade pattern?

  • When you want to provide a simple interface to a complex subsystem. Most patterns, when applied, result in many small domain specific classes. This makes the subsystem more reusable and customisable, but it also becomes harder to use for clients that don't need to customize it. A facade can provide a simple default view of the subsystem that is good enough for most clients
  • There are many dependencies between clients and the implementation classes of an abstraction. Introduce a facade to decouple the subsystem from clients and other subsystems, thereby promoting subsystem independence and portability.
  • Use a facade to define an entry point to each subsystem level. If subsystems are dependent, then you can simplify the dependencies between them by making them communicate with each other solely through their facades.
The Facade pattern is generally implemented for the sake of the Subsystem consumers, not so much the creators, though by simplifying the use of the subsystem this benefits the creators by limiting the amount of questions the consumers will have to make.

Friday, 1 March 2024

Adapter pattern

The (composite) adapter pattern is exactly what it sounds like, think of one of those universal adapters you know the ones; when you travel abroad and you need to plug a North American plug into a European outlet or vice versa.



In development we have the same idea, often times we have data in the form of an object, this could be a class that is represented by an interface, or maybe just a class; whatever the case is, we need to consume that data in a function that though our object has everything we need, it does not implement the expected interface or type which our function consumes. 

For example let's say that we have the following TypeScirpt interface and class for a person


export interface IPerson {
birthDay: number;
birthMoth: number;
birthYear: number;

firstName: string;
lastName: string;

getAge(): number;
getFullName(): string;
}

export default class Person implements IPerson {
birthDay: number;
birthMoth: number;
birthYear: number;
firstName: string;
lastName: string;

constructor(birthDay: number, birthMonth: number, birthYear: number, firstName: string, lastName: string) {
this.birthDay = birthDay;
this.birthMoth = birthMonth;
this.birthYear = birthYear;

this.firstName = firstName;
this.lastName = lastName;
}

getAge(): number {
const today = new Date(Date.now());
let age = today.getFullYear() - this.birthYear;

if (this.birthYear > today.getFullYear() -age)
age--;
return age;
}

getFullName(): string {

return `${this.firstName} ${this.lastName}`
}
}

Keep in mind this is a contrived example, let's say our solution has a function that requires an IHuman interface which looks like the following.


export interface IHuman {
birthDate: Date;
fullName: string;

getAge():number;
}

What we can do is to create an PersonToHumanAdapter, you can think of this as a wrapper that takes in IPerson as a constructor variable and wraps it in a class that implements the IHuman interface. 

As UML this can look like the following 

It's rather trivial, we create a PersonToHumanAdapter class, which implements the IHuman interface and takes in the IPerson implementation, it then adapts the IPerson implementation to an IHuman interface. Sometime this is also referred to as just a simple wrapper, however I find the word Adapter is more descriptive. 


export class PersonToHumanAdapter implements IHuman{
birthDate: Date;
fullName: string;

constructor(p: IPerson) {
this.birthDate = new Date(p.birthYear, p.birthMoth, p.birthDay);
this.fullName = p.getFullName();
this.getAge = p.getAge;
}

getAge(): number {
throw new Error("Method not implemented.");
}
}

An important distinction to make is that adapters are dumb, they do not modify or add functionality, they just make an implementation of a class usable by a client. In the above example we simply assigned all of the members of the person object to our adapter, however we could have created a private person variable and leveraged it instead.


export class PersonToHumanAdapter implements IHuman{
private _p: IPerson
public get birthDate() {
const p = this._p;
return new Date(p.birthYear, p.birthMoth, p.birthDay);;
}
public set birthDate(birthDate: Date) {
const p = this._p;
p.birthDay = birthDate.getDay();
p.birthMoth = birthDate.getMonth();
p.birthYear = birthDate.getFullYear();
}
public get fullName(){
return this._p.getFullName();
}
public set fullName(fullName: string) {
const p = this._p;
p.firstName = fullName.split(" ")[0]
p.lastName = fullName.split(" ")[1];
}

constructor(p: IPerson) {
this._p = p;
}

getAge(): number {
return this._p.getAge();
}
}

At the end of the day it really depends on the nuances of your particular requirements.

A more generic visualisation would be the following

Think of the client as "Our" program, the thing that is trying to use a function called request, which is defined in the Target interface. Our adapter implements the Target interface and tells our client that yes I have an implementation of the Request function, the adapter wraps the adaptee and lets our "Program" use the "Adaptee" even though the Adaptee does not implement the correct interface. The Adapter wraps the SpecificRequest function which is defined in the Adaptee and returns it's result to the client when called via the adapter.