{"id":26773142,"url":"https://github.com/nichitaa/design-patterns","last_synced_at":"2025-08-28T09:12:18.384Z","repository":{"id":133103991,"uuid":"407949674","full_name":"nichitaa/design-patterns","owner":"nichitaa","description":"CLI application that implements some creational, structural and behavioral design patterns in TypeScript.","archived":false,"fork":false,"pushed_at":"2021-12-03T20:05:30.000Z","size":3945,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-29T01:38:34.657Z","etag":null,"topics":["adapter-pattern","builder-pattern","design-patterns","facade-pattern","factory-method-pattern","inquirer","iterator-pattern","observer-pattern","proxy-pattern","singleton-pattern","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nichitaa.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-09-18T19:27:00.000Z","updated_at":"2023-03-07T07:31:13.000Z","dependencies_parsed_at":null,"dependency_job_id":"468a4b7a-f772-4688-8e8f-d299e7114614","html_url":"https://github.com/nichitaa/design-patterns","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/nichitaa/design-patterns","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nichitaa%2Fdesign-patterns","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nichitaa%2Fdesign-patterns/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nichitaa%2Fdesign-patterns/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nichitaa%2Fdesign-patterns/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nichitaa","download_url":"https://codeload.github.com/nichitaa/design-patterns/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nichitaa%2Fdesign-patterns/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":272475367,"owners_count":24940718,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-28T02:00:10.768Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["adapter-pattern","builder-pattern","design-patterns","facade-pattern","factory-method-pattern","inquirer","iterator-pattern","observer-pattern","proxy-pattern","singleton-pattern","typescript"],"created_at":"2025-03-29T01:36:07.328Z","updated_at":"2025-08-28T09:12:18.376Z","avatar_url":"https://github.com/nichitaa.png","language":"TypeScript","readme":"\u003e # *Design Patterns*\n\u003e\n\u003e FAF 192 Y3-S1\n\u003e\n\u003e Pasecinic Nichita\n\n\n\nThe purpose of this laboratory work is studying programming design patterns. This is a cli application with functionality of  running a demo on pattern or displaying the actual implementation for a specific pattern.\n\nTo run it:\n\n```bash\n$ git clone https://github.com/nichitaa/design-patterns\n$ npm install # install dependencies\n$ npm start # compile and run the builded version (tsc \u0026\u0026 node build/index.js)\n\n```\n\n![gif](https://github.com/nichitaa/design-patterns/blob/main/gif/gif1.gif)\n\n### **Creational design patterns**\n\nCreational patterns provide various object creation mechanisms, which increase flexibility and reuse of existing code.\n\n**Singleton** is a creational design pattern, which ensures that only one object of its kind exists and provides a single point of access to it for any other code. Singleton has almost the same pros and cons as global variables. Although they’re super-handy, they break the modularity of your code and it could be implemented as follows:\n\n```typescript\nnamespace SingletonPattern {\n    export class Singleton {\n        private static _instance: Singleton;\n\n        private constructor() {}\n\n        public static getInstance(): Singleton {\n            if (!Singleton._instance) {\n                Singleton._instance = new Singleton();\n            }\n            return Singleton._instance;\n        }\n    }\n}\n```\n\n**Builder** is a creational design pattern, which allows constructing complex objects step by step. Unlike other creational patterns, Builder doesn’t require products to have a common interface. That makes it possible to produce different products using the same construction process. It could be implemented as follows:\n\n```typescript\nnamespace BuilderPattern {\n    export class RestaurantBuilder {\n        private readonly name: string;\n        private address: string;\n\n        constructor(name: string) {\n            this.name = name;\n        }\n\n        get Name() {\n            return this.name;\n        }\n\n        setAddress(val: string) {\n            this.address = val;\n            return this;\n        }\n\n        get Address() {\n            return this.address;\n        }\n\n        // ... other attributes\n        \n        build(): Restaurant {\n            return new Restaurant(this);\n        }\n    }\n\n    export class Restaurant {\n        private readonly name: string;\n        private readonly address: string;\n        private readonly rating: number;\n        private readonly cooksNo: number;\n        private readonly waitersNo: number;\n\n        constructor(builder: RestaurantBuilder) {\n            this.name = builder.Name;\n            this.address = builder.Address;\n            this.rating = builder.Rating;\n            this.cooksNo = builder.CooksNo;\n            this.waitersNo = builder.WaitersNo;\n        }\n\n        get Name() {\n            return this.name;\n        }\n        // ... other attributes\n    }\n}\n```\n\n**Factory method** is a creational design pattern which solves the problem of creating product objects without specifying their concrete classes. Factory Method defines a method, which should be used for creating objects instead of direct constructor call (`new` operator). Subclasses can override this method to change the class of objects that will be created. It could be implemented as follows:\n\n```typescript\nnamespace FactoryMethodPattern {\n\n    /***\n     * Types of food in our Food factory\n     * */\n    export enum FoodTypesEnum {\n        MEAT = 'MEAT',\n        VEGETABLE = 'VEGETABLE'\n    }\n\n    /***\n     * Abstract interface of a concrete product (food) from our factory\n     * */\n    export interface IFood {\n        name: string;\n        preparationTime: number;\n        type: FoodTypesEnum;\n\n        prepare(args?: any): void;\n    }\n\n    /***\n     * The method on both Concrete products will do the same thing, so we can use inheritance\n     * where Food class implements the Abstract product (IFood)\n     * */\n    export class Food implements IFood {\n        name: string;\n        preparationTime: number;\n        type: FoodTypesEnum;\n\n        constructor(name: string, time: number, type: FoodTypesEnum) {\n            this.name = name;\n            this.preparationTime = time;\n            this.type = type;\n        }\n\n        prepare(args?: any): void {\n            console.log(`${this.type} Food: ${this.name} was prepared in: ${this.preparationTime} time units.`);\n        }\n    }\n\n    /***\n     * Concrete products\n     * */\n    export class VegetableFood extends Food {\n        constructor(name: string, time: number, type: FoodTypesEnum) {\n            super(name, time, type);\n        }\n    }\n\n    export class MeatFood extends Food {\n        constructor(name: string, time: number, type: FoodTypesEnum) {\n            super(name, time, type);\n        }\n    }\n\n    /***\n     * Our Food Factory\n     * */\n    export namespace FoodFactory {\n        export const createFood = (type: FoodTypesEnum, name: string, time: number) =\u003e {\n            switch (type) {\n                case FactoryMethodPattern.FoodTypesEnum.MEAT: {\n                    return new MeatFood(name, time, type);\n                }\n                case FoodTypesEnum.VEGETABLE: {\n                    return new VegetableFood(name, time, type);\n                }\n                default: {\n                    throw new Error(`No such food type: ${type}`);\n                }\n            }\n        };\n    }\n}\n```\n\n\n\n### **Structural design patterns**\n\nIn software engineering, the Structural Design Patterns are concerned with how classes and objects are composed to form larger structures. Structural class patterns use inheritance to create a hierarchy of classes/abstractions, but the structural object patterns use composition which is generally a more flexible alternative to inheritance.\n\n**Facade** is a structural design pattern that provides a simplified (but limited) interface to a complex system of classes, library or framework and it could be implemented as follows:\n\n```typescript\nnamespace FacadePattern {\n    export interface IShape {\n        draw(): string;\n    }\n\n    class Square implements IShape {\n        public draw(): string {\n            return 'square - ⬜';\n        }\n    }\n\n    class Circle implements IShape {\n        public draw(): string {\n            return 'circle - ◯';\n        }\n    }\n\n    export class ShapeFacade {\n        private circle: Circle;\n        private square: Square;\n\n        constructor() {\n            this.circle = new Circle();\n            this.square = new Square();\n        }\n\n        public drawCircle(): string {\n            return this.circle.draw();\n        }\n\n        public drawSquare(): string {\n            return this.square.draw();\n        }\n        \n        // ... other facade parts methods\n    }\n\n}\n```\n\n**Adapter** is a structural design pattern, which allows incompatible objects to collaborate. The Adapter acts as a wrapper between two objects. It catches calls for one object and transforms them to format and interface recognizable by the second object. It could be implemented as follows:\n\n```typescript\nnamespace AdapterPattern {\n    export interface ITarget {\n        getMessage(): string;\n    }\n\n    export class Target implements ITarget {\n        getMessage(): string {\n            return 'Default message from Target class';\n        }\n    }\n\n    export class Adaptee {\n        public getAdapteeMessage() {\n            return 'Message from adaptee: zdarova';\n        }\n    }\n\n    export class Adapter extends Target {\n        private _adaptee: Adaptee;\n\n        constructor(adaptee: Adaptee) {\n            super();\n            this._adaptee = adaptee;\n        }\n\n        public getMessage(): string {\n            const result = this._adaptee.getAdapteeMessage().toUpperCase()\n            return `Adapter Upper case message: ${result}`\n        }\n    }\n}\n```\n\n**Proxy** is a structural design pattern that provides an object that acts as a substitute for a real service object used by a client. A proxy receives client requests, does some work (access control, caching, etc.) and then passes the request to a service object. It could be implemented as follows:\n\n```typescript\nnamespace ProxyPattern {\n\n    export interface IService {\n        getUserData(params?: any): void;\n    }\n\n    /***\n     * Real subject (service) the proxy will redirect to\n     * */\n    export class SensitiveUserService implements IService {\n        getUserData() {\n            return {'username': 'nichitaa', 'password': 'strongPassword', 'university': 'UTM'};\n        }\n    }\n\n    /***\n     * Proxy with simple auth / load balance / caching\n     * */\n    export class Proxy implements IService {\n        private readonly proxyName: string;\n        private userService: SensitiveUserService;\n        private limit: number; // max number of requests\n        private cachedData: any;\n\n        constructor(name: string) {\n            this.proxyName = name;\n            this.userService = new SensitiveUserService();\n            this.limit = 2;\n            this.cachedData = null;\n        }\n\n        getUserData(authToken: string) {\n            if (this.checkAuth(authToken)) {\n                if (this.loadBalance()) {\n                    if (this.cache()) {\n                        // all checks have passed, do redirect to the real service\n                        this.cachedData = this.userService.getUserData();\n                        return this.cachedData;\n                    } else {\n                        return this.cachedData;\n                    }\n                } else {\n                    return `[${this.proxyName}] No more available requests`;\n                }\n            } else {\n                return `[${this.proxyName}] Not Authorized`;\n            }\n        }\n\n        cache() {\n            return !this.cachedData;\n        }\n\n        loadBalance() {\n            if (this.limit === 0) return false;\n            else {\n                this.limit--;\n                return true;\n            }\n        }\n\n        checkAuth(token: string) {\n            return token === 'securedToken';\n        }\n    }\n}\n```\n\n### **Behavioral design patterns**\n\nIn software engineering, behavioral design patterns have the purpose of identifying common communication patterns between different software entities. By doing so, these patterns increase flexibility in carrying out this communication.\n\n**Iterator** is a behavioral design pattern that allows sequential traversal through a complex data structure without exposing its internal details.\n\n```typescript\nnamespace IteratorPattern {\n\n    interface Iterator\u003cItemType, ReturnType\u003e {\n        /**\n         * @returns the item at current position\n         */\n        current(): ItemType;\n\n        /**\n         * @returns the item following the logic described below\n         */\n        next(): ReturnType;\n\n        /**\n         * @returns the current position\n         */\n        key(): number;\n\n        /**\n         * @returns boolean value, true if in collection are still more items\n         * to be traversed, else returns false and resets the current position\n         */\n        valid(): boolean;\n\n        /**\n         * @returns void resets the position to initial\n         */\n        rewind(): void;\n    }\n\n    interface Aggregator\u003cItemType, ReturnType\u003e {\n        getIterator(): Iterator\u003cItemType, ReturnType\u003e;\n    }\n\n    /**\n     * Concrete implementation\n     */\n    interface IteratorItem {\n        type: 'string' | 'number' | 'idk',\n        value?: string | number\n    }\n\n    interface IteratorReturn {\n        idx: number;\n        val: IteratorItem['value'];\n    }\n\n    /**\n     * Logic for this CustomIterator is straightforward,\n     * it will just skip the elements with type: 'idk' in collection,\n     * and additional it will type check the value of the item property\n     * to match the specified type\n     */\n    export class CustomIterator implements Iterator\u003cIteratorItem, IteratorReturn\u003e {\n        private position: number = 0;\n\n        constructor(private collection: CustomCollection) {}\n\n        current(): IteratorItem {\n            return this.collection.getItems()[this.position];\n        }\n\n        key(): number {\n            return this.position;\n        }\n\n        next(): IteratorReturn {\n            const item = this.collection.getItems()[this.position];\n            switch (item.type) {\n                case 'string': {\n                    if (item.value \u0026\u0026 typeof item.value !== 'string') {\n                        throw new Error(`Type is string but was given the value: ${item.value}`);\n                    }\n                    break;\n                }\n                case 'number': {\n                    if (item.value \u0026\u0026 typeof item.value !== 'number') {\n                        throw new Error(`Type is number but was given the value: ${item.value}`);\n                    }\n                    break;\n                }\n                case 'idk': {\n                    // skip items with 'idk' types\n                    this.position++;\n                    return this.next();\n                }\n                default: {\n                    throw new Error(`Unhandled type: ${item.type}`);\n                }\n            }\n            const res = {idx: this.position, val: item.value};\n            this.position++;\n            return res;\n        }\n\n        rewind(): void {\n            this.position = 0;\n        }\n\n        valid(): boolean {\n            const exists = this.position \u003c this.collection.getCount();\n            if (exists) {\n                return exists;\n            } else {\n                this.rewind();\n                return exists;\n            }\n        }\n    }\n\n    /**\n     * A CustomCollection that can use the CustomIterator logic to\n     * iterate in collection items\n     */\n    export class CustomCollection implements Aggregator\u003cIteratorItem, IteratorReturn\u003e {\n        private items: IteratorItem[] = [];\n\n        public getItems(): IteratorItem[] {\n            return this.items;\n        }\n\n        public getCount(): number {\n            return this.items.length;\n        }\n\n        public add(item: IteratorItem): void {\n            this.items.push(item);\n        }\n\n        public getIterator(): Iterator\u003cIteratorItem, IteratorReturn\u003e {\n            return new CustomIterator(this);\n        }\n\n    }\n}\n```\n\n**Observer** pattern provides a way to subscribe and unsubscribe to and from these events for any object that implements a subscriber interface.\n\n```typescript\nnamespace ObserverPattern {\n    interface Subject {\n        registerObserver(o: Observer);\n\n        removeObserver(o: Observer);\n\n        notifyObservers();\n    }\n\n    interface Observer {\n        update(course: number);\n    }\n\n    /**\n     * Our Subject class that will be providing the current price of BTC_USD\n     */\n    export class TradingPlatform implements Subject {\n        private observers: Observer[] = [];\n\n        private BTC_USD: number;\n\n        setBTCUSDPrice(price: number) {\n            console.log(`[live] BTC-USD: ${price}`);\n            this.BTC_USD = price;\n            this.notifyObservers();\n        }\n\n        registerObserver(o: Observer) {\n            this.observers.push(o);\n        }\n\n        removeObserver(o: Observer) {\n            const idx = this.observers.indexOf(o);\n            this.observers.splice(idx, 1);\n        }\n\n        notifyObservers() {\n            for (let o of this.observers) {\n                o.update(this.BTC_USD);\n            }\n        }\n    }\n\n    /**\n     * Our Observers that will consume and get notified of the BTC-USD price provided by the Subject\n     */\n    export class WalletDashboard implements Observer {\n        /**\n         * @param courseProvider - is the actual TradingPlatform in this example\n         */\n        constructor(private courseProvider: Subject) {\n            this.courseProvider.registerObserver(this);\n        }\n\n        update(BTC_USD_Price: number) {\n            if (BTC_USD_Price \u003e 60000) {\n                console.log('BTC price is higher then 60k$, app recommendation is to sell it!');\n            } else {\n                console.log('BTC course is less then 60k$, app recommendation is to buy it!');\n            }\n        }\n    }\n\n    export class NewsApp implements Observer {\n        constructor(private courseProvider: Subject) {\n            this.courseProvider.registerObserver(this);\n        }\n\n        update(BTC_USD_Price: number) {\n            console.log(`Breaking NEWS: Is Bitcoin real money ? it is ${BTC_USD_Price}$ now!`);\n        }\n    }\n}\n```\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnichitaa%2Fdesign-patterns","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnichitaa%2Fdesign-patterns","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnichitaa%2Fdesign-patterns/lists"}