---
# System prepended metadata

title: On Premises and Design patterns applied
tags: [Design Pattern]

---

# On Premises and Design patterns applied

###### tags: `Design Pattern`

## On premises

### What is on premises?
On-premises refers to IT infrastructure hardware and software applications that are **hosted on-site (就地部屬)**. This contrasts with IT assets that are hosted by a public cloud platform or remote data center. Businesses have more control of on-premises IT assets by maintaining the performance, security and upkeep, as well as the physical location.

### Why is there still demand for on premises?

**Disadvantages of cloud service: Privacy Issues**

The availability of information can be breached when usingcloud computing. Your data can be accessed by unauthorizedentities or even modified.

### The reason of applying design patterns

- Keep single code base for a product for both web app and on premise with the consideration for future update

- Yet, APIs and request header token for the 2 scenario are different for identical purpose. Ex: creating a project through api
    - web app: stored in the cloud. Need CRUD
    - on premise: creating project stored in local SQLite db. Need CRUD

- Encapsulate the logic (ex: which api to call) in one place, making rest of the code base (ex: where one of the api need to be called) remained unchanged

- ETC (Easy to change)


<br>

## Applied design patterns brief intro

### Strategy design pattern
> Strategy is a behavioral design pattern that lets you define a family of algorithms, put each of them into a separate class, and make their objects interchangeable.

> Enable the exact behavior of a system to be selected at run-time

#### Life analogy

<u>Making hot drink</u>
- Making coffee
    - water preparation: Boil water 200ml, lower temperature to 90 degree
    - making drink: grind one spoon of coffee beans into power, slowing pour hot water in

- Making black tea
    - water preparation: Boil water 500ml
    - making drink: put teabag into the bottle, pour the water in, remove teabag after 2 min

:::success
**Why applying this design pattern?**

Both web app and on premise have identical method (ex: CRUD project)
:::

#### Code example

```typescript=
/**
 * The Context defines the interface of interest to clients.
 */
class Context {
    private strategy: Strategy;

    /**
     * Usually, the Context accepts a strategy through the constructor, but also
     * provides a setter to change it at runtime.
     */
    constructor(strategy: Strategy) {
        this.strategy = strategy;
    }

    /**
     * Usually, the Context allows replacing a Strategy object at runtime.
     */
    public setStrategy(strategy: Strategy) {
        this.strategy = strategy;
    }

    /**
     * The Context delegates some work to the Strategy object instead of
     * implementing multiple versions of the algorithm on its own.
     */
    public doSomeBusinessLogic(): void {
        // ...

        console.log('Context: Sorting data using the strategy (not sure how it\'ll do it)');
        const result = this.strategy.doAlgorithm(['a', 'b', 'c', 'd', 'e']);
        console.log(result.join(','));

        // ...
    }
}

/**
 * The Strategy interface declares operations common to all supported versions
 * of some algorithm.
 *
 * The Context uses this interface to call the algorithm defined by Concrete
 * Strategies.
 */
interface Strategy {
    doAlgorithm(data: string[]): string[];
}

/**
 * Concrete Strategies implement the algorithm while following the base Strategy
 * interface. The interface makes them interchangeable in the Context.
 */
class ConcreteStrategyA implements Strategy {
    public doAlgorithm(data: string[]): string[] {
        return data.sort();
    }
}

class ConcreteStrategyB implements Strategy {
    public doAlgorithm(data: string[]): string[] {
        return data.reverse();
    }
}

/**
 * The client code picks a concrete strategy and passes it to the context. The
 * client should be aware of the differences between strategies in order to make
 * the right choice.
 */
const context = new Context(new ConcreteStrategyA());
console.log('Client: Strategy is set to normal sorting.');
context.doSomeBusinessLogic();

console.log('');

console.log('Client: Strategy is set to reverse sorting.');
context.setStrategy(new ConcreteStrategyB());
context.doSomeBusinessLogic();
```

log result
```
Client: Strategy is set to normal sorting.
Context: Sorting data using the strategy (not sure how it'll do it)
a,b,c,d,e

Client: Strategy is set to reverse sorting.
Context: Sorting data using the strategy (not sure how it'll do it)
e,d,c,b,a
```


<br>

### Factory design pattern
> Factory method is a creational design pattern which solves the problem of creating product objects without specifying their concrete classes.

> A component responsible solely for the wholesale creation of objects

#### Life analogy

<u>Logistic management</u>

<img src="https://refactoring.guru/images/patterns/content/factory-method/factory-method-en.png?id=cfa26f33dc8473e803fadae0d262100a"/>

:::success
**Why applying this design pattern?**

We want to decide with set of CRUD api to use without passing params everytime
:::

#### Code sample

```typescript
/**
 * The Creator class declares the factory method that is supposed to return an
 * object of a Product class. The Creator's subclasses usually provide the
 * implementation of this method.
 */
abstract class Creator {
    /**
     * Note that the Creator may also provide some default implementation of the
     * factory method.
     */
    public abstract factoryMethod(): Product;

    /**
     * Also note that, despite its name, the Creator's primary responsibility is
     * not creating products. Usually, it contains some core business logic that
     * relies on Product objects, returned by the factory method. Subclasses can
     * indirectly change that business logic by overriding the factory method
     * and returning a different type of product from it.
     */
    public someOperation(): string {
        // Call the factory method to create a Product object.
        const product = this.factoryMethod();
        // Now, use the product.
        return `Creator: The same creator's code has just worked with ${product.operation()}`;
    }
}

/**
 * Concrete Creators override the factory method in order to change the
 * resulting product's type.
 */
class ConcreteCreator1 extends Creator {
    /**
     * Note that the signature of the method still uses the abstract product
     * type, even though the concrete product is actually returned from the
     * method. This way the Creator can stay independent of concrete product
     * classes.
     */
    public factoryMethod(): Product {
        return new ConcreteProduct1();
    }
}

class ConcreteCreator2 extends Creator {
    public factoryMethod(): Product {
        return new ConcreteProduct2();
    }
}

/**
 * The Product interface declares the operations that all concrete products must
 * implement.
 */
interface Product {
    operation(): string;
}

/**
 * Concrete Products provide various implementations of the Product interface.
 */
class ConcreteProduct1 implements Product {
    public operation(): string {
        return '{Result of the ConcreteProduct1}';
    }
}

class ConcreteProduct2 implements Product {
    public operation(): string {
        return '{Result of the ConcreteProduct2}';
    }
}

/**
 * The client code works with an instance of a concrete creator, albeit through
 * its base interface. As long as the client keeps working with the creator via
 * the base interface, you can pass it any creator's subclass.
 */
function clientCode(creator: Creator) {
    // ...
    console.log('Client: I\'m not aware of the creator\'s class, but it still works.');
    console.log(creator.someOperation());
    // ...
}

/**
 * The Application picks a creator's type depending on the configuration or
 * environment.
 */
console.log('App: Launched with the ConcreteCreator1.');
clientCode(new ConcreteCreator1());
console.log('');

console.log('App: Launched with the ConcreteCreator2.');
clientCode(new ConcreteCreator2());
```

log result
```
App: Launched with the ConcreteCreator1.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with {Result of the ConcreteProduct1}

App: Launched with the ConcreteCreator2.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with {Result of the ConcreteProduct2}
```

<br>

### Singleton design pattern
> Singleton is a creational design pattern that lets you ensure that a class **has only one instance**, while providing a global access point to this instance.

<img src="https://refactoring.guru/images/patterns/content/singleton/singleton.png?id=108a0b9b5ea5c4426e0afa4504491d6f">

:::success
**Why applying this design pattern?**

There's no need to create the object which contains identical method everytime
:::

#### Code sample

```typescript
/**
 * The Singleton class defines the `getInstance` method that lets clients access
 * the unique singleton instance.
 */
class Singleton {
    private static instance: Singleton;

    /**
     * The Singleton's constructor should always be private to prevent direct
     * construction calls with the `new` operator.
     */
    private constructor() { }

    /**
     * The static method that controls the access to the singleton instance.
     *
     * This implementation let you subclass the Singleton class while keeping
     * just one instance of each subclass around.
     */
    public static getInstance(): Singleton {
        if (!Singleton.instance) {
            Singleton.instance = new Singleton();
        }

        return Singleton.instance;
    }

    /**
     * Finally, any singleton should define some business logic, which can be
     * executed on its instance.
     */
    public someBusinessLogic() {
        // ...
    }
}

/**
 * The client code.
 */
function clientCode() {
    const s1 = Singleton.getInstance();
    const s2 = Singleton.getInstance();

    if (s1 === s2) {
        console.log('Singleton works, both variables contain the same instance.');
    } else {
        console.log('Singleton failed, variables contain different instances.');
    }
}

clientCode();
```

log result

```
Singleton works, both variables contain the same instance.
```

<br>

---

## Apply design pattern to required on premises scenario

### module structure

api > service > component/store

```
|---apis
|   |---studioApi
|   |---onPremStudioApi
|---services
|   |
|   |--- userService
|   |           |---index.ts (factory + singleton)
|   |           |---userService.ts (strategy)
|   |           |---onPremUserService.ts (strategy)
|   |   
|   |--- studioServie
|              |---index.ts (factory + singleton)
|              |---studioService.ts (strategy)
|              |---onPremStudioService.ts (strategy)
|
|---store
|
|---view

```

### ETC (Easy to change) discussion