javascript 的设计模式(更新中)

一、本内容采用的typescript来写的,原因是typescript的格式更加的严格,面向对象的写法更加的突出,环境配置以及typescript可参看webpack与typescirpt

注意在引入jquery的时候要用到一个依赖,以方便jquery在ts中使用

npm install --save-dev @types/jquery
//引入用以下方法
import $ = require('jquery');

二、设计模式

1、简单工厂模式

定义: 由一个工厂对象决定创建出哪一种产品类的实例

类型:创建型,但不属于GOF23种设计模式

适用场景:1、工厂类负责创建的对象比较少; 2、客户端(应用层)只知道传入工厂类的参数对于如何创建对象(逻辑)不关心

优点:只需要传入一个参数,就可以获取你所需要的对象而无须知道其创建细节

缺点:工厂类的职责相对过重,增加新的产品需要修改工厂类的判断逻辑,违背开闭原则, 无法实现基于继承的等级结构不利于扩展和维护

例子: 如下

创建工厂类的接口

export interface Video {
    play(): void;
}

创建各个产品,并且实现接口

import {Video} from "./builder";

export class JavaVideo implements Video{
    public play(): void {
    }
}

export class PhpVideo implements Video {
    public play(): void {
    }
}

export class GoVideo implements Video {
    public play(): void {
    }
}

创建调用产品类的入口方法即实现简单工厂方法

import {Video} from "./builder";
import {GoVideo, JavaVideo, PhpVideo} from "./product";

export class GetProduct {
    public static get(str: string): Video {
        if(str == 'java') {
            return new JavaVideo();
        } else if (str == 'php') {
            return new PhpVideo();
        } else if (str == 'go') {
            return new GoVideo();
        }
        return null;
    }
}

客户端调用

import {GetProduct} from "./tools/getProduct";

console.log(GetProduct.get('java'));

2、工厂方法

定义:定义一个创建对象的接口但让实现这个接口的类来决定实例化哪个类工厂方法让类的实例化推迟到子类中进行。

类型:创建型

适用场景:1、创建对象需要大量的代码;2、客户端(应用层)不依赖于产品类如何被创建,实现等细节;3、一个类通过其子类来指定创建哪个对象;

优点:用户只需要关心所需产品对应的工厂,无须关心创建的细节,加入新产品符合开闭原则,提高可扩展性

缺点:类的个数容易过多,增加复杂度; 增加了系统的抽象性和理解难度;

建立产品抽象类

export interface Video {
    play(): void;
}

建立产品类

import {Video} from "./builder";

export class JavaVideo implements Video{
    public play(): void {
    }
}

export class PhpVideo implements Video {
    public play(): void {
    }
}

export class GoVideo implements Video {
    public play(): void {
    }
}

建立产品工厂类

import {Video} from "./builder";
import {GoVideo, JavaVideo, PhpVideo} from "./product";

export abstract class Factory {
    public abstract init(): void;
}

export class JavaVideoFactory extends Factory {
    private video: Video;
    public constructor() {
        super();
        this.video = new JavaVideo();
    }
    public init(): void {
        this.video.play();
    }
}

export class PhpVideoFactory extends Factory {
    private video: Video;
    public constructor() {
        super();
        this.video = new PhpVideo();
    }
    public init(): void {
        this.video.play();
    }
}

export class GoVideoFactory extends Factory {
    private video: Video;
    public constructor() {
        super();
        this.video = new GoVideo();
    }
    public init(): void {
        this.video.play();
    }
}

应用层调用

import {Factory, GoVideoFactory, JavaVideoFactory, PhpVideoFactory} from "./tools/getProduct";

class Test {
    public static getVideo() {
        let java: Factory = new JavaVideoFactory();
        java.init();
        let php: Factory = new PhpVideoFactory();
        php.init();
        let go: Factory = new GoVideoFactory();
        go.init();
    }
}

Test.getVideo();

以上是把所有产品类的实例化全部放到工厂类中,使用时直接调用工厂类

3、抽象工厂模式

定义:抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口

特点:无需指定它们具体的类

类型:创建型

适用场景:1、客户端(应用层)不依赖于产品类实例如何被创建、实现等细节;2、强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量重复的代码; 3、提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现

优点:具体产品在应用层代码隔离,无须关心创建的具体细节; 将同一系列的产品族统一到一起创建

缺点:规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口;增加了系统的抽象性和理解难度

创建产品的接口

/**
 * 视频接口
 */
export interface IVideo {
    play(): void;
}

/**
 * 文章接口
 */
export interface IArticle {
    write(): void;
}

创建课程类

import {IVideo, IArticle} from "./builder";

//java视频的类
export class JavaVideo implements IVideo{
    public play(): void {
        console.log('录制java视频');
    }
}

//java手记的类
export class JavaArticle implements IArticle{
    public write(): void {
        console.log('写java的手记');
    }
}

//php视频的类
export class PhpVideo implements IVideo{
    public play(): void {
        console.log('录制php视频');
    }
}

//php手记的类
export class PhpArticle implements IArticle{
    public write(): void {
        console.log('写php的手记');
    }
}

创建课程组合

import {IArticle, IVideo} from "./builder";
import {JavaArticle, JavaVideo, PhpArticle, PhpVideo} from "./product";

//JAVA课程集合
export class JavaCourse {
    public static getVideo(): IVideo {
        return new JavaVideo()
    }
    public static getArticle(): IArticle {
        return new JavaArticle()
    }
}

//PHP课程集合
export class PhpCourse {
    public static getVideo(): IVideo {
        return new PhpVideo()
    }
    public static getArticle(): IArticle {
        return new PhpArticle()
    }
}

调用

import {JavaCourse, PhpCourse} from "./tools/getProduct";

export class Text{
    public static getJavaCourse() {
        let javaVideo = JavaCourse.getVideo();
        let javaArticle = JavaCourse.getArticle();
        javaVideo.play();
        javaArticle.write();
    }
    public static getPhpCourse() {
        let phpVideo = PhpCourse.getVideo();
        let phpArticle = PhpCourse.getArticle();
        phpVideo.play();
        phpArticle.write();
    }
}

Text.getJavaCourse();
Text.getPhpCourse();

说明: 工厂模式注意产品的层级,而抽象工厂注意产品级,抽象工厂不宜使用在产品结构变化比较经常的地方;

4、建造者模式

 定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示

 特点:用户只需要指定需要建造的类型就可以得到它们,建造过程及细节不需要知道

 类型: 创建型

 优点: 封装性好,创建和使用分离; 扩展性好、建造类之间独立、一定程度上解耦

 缺点:产生多余的Builder对象;产品内部发生变化,建造者都要修改,成本较大

Product(产品)

export default class Product {
    private header: string;
    private body: string;
    private foot: string;
    public constructor() {}
    public buildHeader(header: string) {
        this.header = header;
    }
    public getHeader(): string {
        return this.header;
    }
    public buildBody(body: string) {
        this.body = body;
    }
    public getBody(): string {
        return this.body;
    }
    public buildFoot(foot: string) {
        this.foot = foot;
    }
    public getFoot(): string {
        return this.foot;
    }
}

抽象建造者Builder

export interface IBuilder {
    buildHeader():void
    buildBody(): void
    buildFoot(): void
    getBuild(): Product
}

具体建造者ConcreteBuilder

export class ConcreteBuilder implements IBuilder{
    private product: Product;
    public constructor() {
        this.product = new Product();
    }

    public buildHeader(): void {
        this.product.buildHeader('this is header');
    }

    public buildBody(): void {
        this.product.buildBody('this is body');
    }

    public buildFoot(): void {
        this.product.buildFoot('this is foot');
    }

    public getBuild(): Product {
        return this.product;
    }
}

指挥者Director

export class Director {
    public create() {
        let build = new ConcreteBuilder();
        build.buildHeader();
        build.buildBody();
        build.buildFoot();
        return build.getBuild();
    }
}

调用

let p = new Director();
console.log(p.create());
//输出 Product对象

5、单例模式

 定义:保证一个类仅有一个实例,并且提供一个全局访问点

 类型:创建型

 适用场景:1、确保任何情况下都绝对只有一个实例

 优点:1、在内存里只有一个实例,减少了内存的开销;2、可以避免对资源的多重占用;3、设置了全局访问点,严格的控制了访问

 缺点:没有接口, 扩展困难

 特点:私有构造器, 可延迟加载

例子:如下:

// 饿汉式
export class SuperMan {
    private static instance = new SuperMan();
    private superManName: string = 'jack';
    public static getInstance(): SuperMan {
        return SuperMan.instance;
    }
    private constructor() {
    }
    public save(name: string, place: string): void {
        console.log(`超人${this.superManName}在${place}救了${name}`);
    }
}
//懒汉式
export class SuperMan {
    private static instance: any;
    private superManName: string = 'jack';
    public static getInstance(): SuperMan {
        if(!(SuperMan.instance instanceof SuperMan)) {
            SuperMan.instance = new SuperMan();
        }
        return SuperMan.instance;
    }
    private constructor() {
    }
    public save(name: string, place: string): void {
        console.log(`超人${this.superManName}在${place}救了${name}`);
    }
}

调用:

import {SuperMan} from "./tools/model";

let man1 = SuperMan.getInstance();
let man2 = SuperMan.getInstance();
console.log(man1 === man2);
//输出 true
//永远只通被实例化一次
man1.save('john', 'shanghai');

说明:可以保证目标对象只能被实例化一次,可以减少内存的开销

 6、原型模式(创建型)

 定义: 指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象

 类型: 创建型

 特点:不需要知道任何创建的细节,不调用构造函数

 适用场景:1、类初始化消耗较多资源;2、new产生的一个对象要非常繁琐的过程(数据准备,访问权限等);3、构造函数比较复杂;4、循环体中产生大量的对象时;

 优点:1、原型模式性能比直接new一个对象的性能高;2、简化创建过程

 缺点:1、必需配备克隆方法;2、对克隆复杂对象或对克隆出的对象进行复杂改造时,容易引入风险

 注意:在java或者php中是有单独的克隆的方法调用,但是在javascript语法中,是没有克隆 的方法,那么就可以使用 Object.create来实现克隆的方法

例子:

创建接口

export interface IUnit {
    init(num: number): void
}
export interface IProduct {
    init(): void
}

创建最小单元类

import {IUnit} from "./builder";

export class Item implements  IUnit{
    private item: string;
    private timer: any;
    private num: number = 0;
    private target: number = 10;
    public constructor(name: string) {
        this.item = name;
    }

    private run(): void {
        if(this.num >= this.target) {
            clearInterval(this.timer);
        } else {
            console.log(`${this.item}正在制作中...${this.num ++}s`)
        }
    }

    public init(num: number): void {
        clearInterval(this.timer);
        this.target = num;
        this.timer = setInterval(this.run.bind(this), 2000);
    }
 }

调用最小单元的类

import {IProduct, IUnit} from "./builder";
import {Item} from "./item";

export class Product implements IProduct {
    private _name: string;
    private unit: IUnit;
    private _target: number = 10;

    public constructor(name: string) {
        this._name = name;
    }

    public set name(val: string) {
        this.unit = null;
        this._name = val;
    }

    public set target(val: number) {
        this._target = val;
    }


    public init(): void {
        this.unit = new Item(this._name);
        this.unit.init(this._target);
    }

}

假定上面实现的功能比较复杂,这时个利用原型模式实现多个类,调用类

import {Product} from "./tools/product";

export class Test {
    public static init(name: string): void {
        let orin = new Product(name);
        orin.init();
        let first = Object.create(orin);
        first.name = 'are you ok????';
        first.target = 11;
        first.init();
        let second = Object.create(orin);
        second.name = 'today is good day';
        second.target = 12;
        second.init();
    }
}
Test.init('this is first');

说明:在java中为了避免过度的实例化类面导致的内存开销,或者其他原因,那么前端在开发过程中也可以按照这个思路来

7、外观模式

 定义:外观模式又叫门面模式,提供了一个统一的接口,用来访问子系统中的一群接口

 外观模式定义了一个高层接口,让子系统更容易使用

 类型:结构型

 适用场景:1、子系统越来越复杂,增加外观模式提供简单的调用接口;2、构建多层系统结构,利用外观对象作为每层的入口,简化层间调用;

 优点:1、简化了调用过程,无需了解深入子系统,防止带来风险;2、减少系统依赖、松散耦合;3、更好的划分访问的层次;4、外观模式符合迪米特法则,即最少知道原则;

 缺点:1、增加子系统、扩展子系统行为容易引入风险; 2、不符合开闭原则

 与外观模式相关的设计模式:1、外观模式与中介者模式:外观模式关注的是外界与子系统之间的交互,而中介者模式关注的是子系统内部的交互;2、外观模式与单例模式:通常可以把外观模式中的外观对象作成单例模式,两者之间进行结合使用;3、外观模式与抽象工厂模式:外观类可以通过抽象工厂获取子系统的实例,子系统可以通过内部对外观类进行屏蔽,这也是两者之间的交互了。

 假定一个购买产品的过程:产品类

export class Goods {
    private name: string = 'computer';
    private cost: number = 4500;
    public getName(): string {
        return this.name;
    }
    public getCost(): number {
        return this.cost;
    }
}

较验资金是否足够的较验证系统 

import {Goods} from "./goods";

export class Match {
    private money: number;
    private goods: Goods;
    public constructor(money: number, goods: Goods) {
        this.money = money;
        this.goods = goods;
    }
    public check(): boolean {
        if(this.money >= this.goods.getCost()) {
            console.log(`你的钱是足够支付${this.goods.getName()}`);
            return true;
        }
        console.log(`你的钱是不足以支付${this.goods.getName()}这个商品的`);
        return false;
    }
}

支付产品的支付系统

import {Goods} from "./goods";

export class PayFor {
    private goods: Goods;
    public constructor(goods: Goods) {
        this.goods = goods;
    }
    public payMoney() {
        console.log(`支付成功${this.goods.getName()}成功`);
        return true;
    }
}

物流系统 

export class Transport {
    private transNo: number = 100001;
    public startPass(): void {
        console.log(`物品已经寄出成功,单号是${this.transNo}`);
    }
}

外观模式的实现

import {Goods} from "./goods";
import {Match} from "./match";
import {PayFor} from "./pay";
import {Transport} from "./transport";

export class Appear {
    private goods: Goods;
    private money: number;
    public constructor(goods: Goods, money: number) {
        this.goods = goods;
        this.money = money;
    }
    public getShift(): void {
        let check = new Match(this.money, this.goods);
        if(check.check()) {             //较验成功
            let Pay = new PayFor(this.goods);
            if(Pay.payMoney()) {
                new Transport().startPass();
            }
        }
    }
}

客户端调用

import {Goods} from "./tools/goods";
import {Appear} from "./tools/appear";

let goods = new Goods();
new Appear(goods, 8000).getShift();

说明:客户端在调用的时候无需知道里面的实现逻辑,只需要调用外观模式,那么就可以实现整个逻辑

 8、装饰者模式

定义:在不改变原有对象的基础上,将功能附加到对象上

提供了比继承更有弹性的替代方案(扩展原有对象功能)

类型:结构型

适用场景:1、扩展一个类的功能或给一个类添加附加职责;2、动态的给一个对象添加功能,这些功能可以再动态的撤消

优点:1、继承的有力补充,比继承灵活,不改变原有的对象的情况下给一个对象扩展功能;2、通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果;

3、符合开闭原则

缺点:1、会出现更多的代码,更多的类,增加程序的复杂性; 2、动态装饰或者多层装饰时会更复杂

与装饰者相关的设计模式:1、装饰者模式与代理模式:装饰者模式关注在一个对象上动态的添加方法而代理模式关注控制一个对对象的访问;2、装饰者模式与适配器模式

例子,早餐的套餐

建立煎饼及煎饼接口

export interface ICake {
    getName(): string
    getCost(): number
}

export class Cake implements ICake {
    public getCost(): number {
        return 5;
    }
    public getName(): string {
        return "煎饼";
    }
}

建立抽象装饰者,实体加鸡蛋的装饰者,实体加牛奶的装饰者

import {ICake} from "./cake";

/**
 * 抽象的装饰者类
 */
export abstract class ADecorate implements ICake{
    private cake: ICake;
    public constructor(cake: ICake) {
        this.cake = cake;
    }

    public getCost(): number {
        return this.cake.getCost();
    }

    public getName(): string {
        return this.cake.getName();
    }
}

/**
 * 加鸡蛋的装饰者
 */
export class EggDecorate extends ADecorate{
    public constructor(cake: ICake) {
        super(cake);
    }
    public getName(): string {
        return super.getName() + '加一个鸡蛋';
    }
    public getCost(): number {
        return super.getCost() + 1;
    }
}

/**
 * 加牛奶的装饰者
 */
export class MilkDecorate extends ADecorate {
    public constructor(cake: ICake) {
        super(cake);
    }
    public getCost(): number {
        return super.getCost() + 3;
    }
    public getName(): string {
        return super.getName() + '加一杯牛奶';
    }
}

调用

import {Cake} from "./tools/cake";
import {EggDecorate, MilkDecorate} from "./tools/decorate";

export class Test {
    public static getFood(): void{
        let cake = new Cake();
        console.log(`${cake.getName()}费用是${cake.getCost()}`);
        let eggCake = new EggDecorate(cake);
        console.log(`${eggCake.getName()}费用是${eggCake.getCost()}`);
        let eggMilkCake = new MilkDecorate(eggCake);
        console.log(`${eggMilkCake.getName()}费用是${eggMilkCake.getCost()}`);
        let eggMilkCake2 = new MilkDecorate(eggMilkCake);
        console.log(`${eggMilkCake2.getName()}费用是${eggMilkCake2.getCost()}`);
    }
}

Test.getFood();

输出  

index.ts:10 煎饼费用是5
index.ts:12 煎饼加一个鸡蛋费用是6
index.ts:14 煎饼加一个鸡蛋加一杯牛奶费用是9
index.ts:16 煎饼加一个鸡蛋加一杯牛奶加一杯牛奶费用是12

 9、适配器模式

 定义:将一个类的接口转换成客户期望的另一个接口

 目的:使原本接口不兼容的类可以一起工作

 类型:结构型

 适用场景:1、已经存在的类,它的方法和需求不匹配时(方法结果相同或相似);2、不是软件设计阶段考虑的设计模式,是随着软件维护,由于不同产品,不同厂家造成功能类似而接口不相同的情况下的解决方案

优点:1、能提高类的透明性和复用,现有的类复用,但不需要改变;2、目标类和适配器类解耦,提高程序扩展性;3、符合开闭原则;

缺点:1、适配器编写过程需要全面考虑,可能会增加系统的复杂性;2、增加系统代码可读的难度;

扩展:1、对象适配器模式:符合组合复用性原则,并且使用委托机制;2、类适配器模式:能过类的继承来实现的(继承和组合进行选择的时候优先选择组合

相关的设计模式:1、适配器模式与外观模式:两者对是现在的类,现存的系统的封装,外观定义了新的接口,而适配器则是复用一个原有的接口,是使两个已有的接口协同工作,而外观则是在现有的系统中提供一个更为方便的访问入口

定义接口类型

/**
 * 数据类型1
 */
export interface IFirstType {
    getFirstName(): string
    getLastName(): string
    getTotalScore(): number
    getCount(): number
}

/**
 * 数据类型2
 */
export interface ISecondType {
    getName(): string
    getCNScore(): number
    getENScore(): number
    getMathScore(): number
}

/**
 * 目标数据类型
 */
export interface ITarget {
    getName(): string
    getAveScore(): number
    getTotalScore(): number
}

定义适配器类型

import {IFirstType, ISecondType, ITarget} from "./DataType";

export class FirstAdapter implements ITarget {
    private data: IFirstType;

    public constructor(data: IFirstType) {
        this.data = data;
    }

    public getName(): string {
        return `${this.data.getFirstName()}${this.data.getLastName()}`;
    }

    public getAveScore(): number {
        return this.data.getTotalScore() / this.data.getCount();
    }

    public getTotalScore(): number {
        return this.data.getTotalScore();
    }
}

export class SecondAdapter implements ITarget {
    private data: ISecondType;
    public constructor(data: ISecondType) {
        this.data = data;
    }
    public getName(): string {
        return this.data.getName();
    }

    public getAveScore(): number {
        return (this.data.getCNScore() + this.data.getENScore() + this.data.getMathScore()) / 3;
    }

    public getTotalScore(): number {
        return this.data.getCNScore() + this.data.getENScore() + this.data.getMathScore();
    }

}

定义数据

import {IFirstType, ISecondType} from "./DataType";

export class First implements IFirstType{
    public getFirstName(): string {
        return "王";
    }

    public getLastName(): string {
        return "明";
    }

    public getTotalScore(): number {
        return 420;
    }

    public getCount(): number {
        return 3;
    }

}

export class Second implements ISecondType {
    getName(): string {
        return "林小红";
    }

    getCNScore(): number {
        return 130;
    }

    getENScore(): number {
        return 118;
    }

    getMathScore(): number {
        return 122;
    }

}

调用,数据转换

import {First, Second} from "./tools/Data";
import {FirstAdapter, SecondAdapter} from "./tools/adapter";
import {ITarget} from "./tools/DataType";

let first: ITarget = new FirstAdapter(new First());
let second: ITarget = new SecondAdapter(new Second());
console.log(first.getName(), first.getAveScore(), first.getTotalScore());
console.log(second.getName(), second.getAveScore(), second.getTotalScore());

 10、享元模式

 定义:提供了减少对象数量从而改善应用所需要的对象结构的方式

 补充:应用共享技术有效的支持大量细粒度的对象

 类型:结构型

 适用场景:1、常常应用于系统底层的开发,以便解决系统的性能问题;2、系统有大量相似的对象、需要缓冲的场景;

 优点:1、减少对象的创建,降低内存中对象的数量,降低系统内存,提高效率;2、减少内存之外的其他资源占用;

 缺点:1、关注内外部状态; 2、使系统,程序的逻辑复杂化;

 相关的设计模式:1、享元模式与代理模式:享元模式的速度相对高于代理模式; 2、享元模式与单例模式;

export interface IManager {
    getReport(): string
}

/**
 * 建立经理对象
 */
export class Manager implements IManager{
    private manager: string;
    public constructor(name: string) {
        this.manager = name;
    }
    public getReport(): string {
        return `this is a ${this.manager} report`;
    }
    public getManageName(): string {
        return this.manager;
    }
}

/**
 * 建立Manager的对象池
 */
export class ManagerFactory {
    private static managerMap: Map<string, Manager> = new Map<string, Manager>();
    public static getManager(partName: string) {
        if(!ManagerFactory.managerMap.has(partName)) {
            console.log('创建了对象', partName);
            ManagerFactory.managerMap.set(partName, new Manager(partName));
        }
        return ManagerFactory.managerMap.get(partName);
    }
}

//调用
let part = ['a', 'b', 'c', 'd'];
for(let i = 0; i< 10; i++) {
    let m = ManagerFactory.getManager(part[Math.floor(Math.random()*4)]);
    console.log(m.getReport());
}

说明:以上对象只创建了四个经理对象,虽然循环了10次或更多次,但只会实例化四次对象

11、组合模式

 定义:将对象组合成树形结构以表示“部份-整体”的层次结构 

 补充:组合模式使客户端对单个对象和组合对象保持一致的方式处理

 类型:结构型

 适用场景:1、希望客户端可以忽略组合对象与单个对象的差异时; 2、处理树形结构时;

 优点:1、清楚地定义分层次的复杂对象,表示对象的全部或部份层次; 2、让客户端忽略了层次的差异,方便对整个层次结构进行控制; 3、简化了客户端代码; 4、符合开闭原则

 缺点:1、限制类型时会较为复杂 ; 2、使类型变得更加抽象;

 相关设计模式:1、组合模式与访问者模式:可以使用访问者模式来访问组合模式中的递归结构;

 建立课程与目录共用的抽象类

export abstract class DirectComp {
    protected getName(): string {
        throw new Error('不支持获取名称的方法');
    }
    protected getPrice(): number {
        throw new Error('不支持获取价格的方法');
    }
    public print(): void {
        throw new Error('不支持打印的方法');
    }
    protected add(course: DirectComp): void {
        throw new Error('不支持添加的方法');
    }
    protected remove(course: DirectComp): void {
        throw new Error('不支持删除的方法');
    }
}

建立课程类,继承抽象类,并且重写其所需要的类

import {DirectComp} from "./DirectComp";

export class Course extends DirectComp{
    private course: string;
    private price: number;
    public constructor(course: string, price: number) {
        super();
        this.course = course;
        this.price = price;
    }
    public getName(): string {
        return this.course;
    }
    public getPrice(): number {
        return this.price;
    }
    public print(): void {
        console.log(`课程名称:${this.course},价格:${this.price}`);
    }
}

建立目录类

import {DirectComp} from "./DirectComp";

export class Directory extends DirectComp {
    private content: Set<DirectComp> = new Set<DirectComp>();
    public add(course: DirectComp): void{
        this.content.add(course);
    }
    public remove(course: DirectComp): void {
        this.content.delete(course);
    }
    public print(): void {
        this.content.forEach(value => {
            value.print();
        })
    }
}

调用

import {Course} from "./tools/Course";
import {Directory} from "./tools/Directory";

export class Test {
    public static get(): void {
        let windowDirect = new Directory();
        let linuxDirect = new Directory();
        let windowCourse = new Course('window课程', 300);
        let linuxCourse = new Course('Linux课程', 500);
        let phpCourse = new Course('php课程', 600);
        let javaCourse = new Course('java课程', 650);
        windowDirect.add(windowCourse);
        windowDirect.add(phpCourse);
        windowDirect.add(javaCourse);
        linuxDirect.add(linuxCourse);
        linuxDirect.add(phpCourse);
        linuxDirect.add(javaCourse);

        windowDirect.print();
        linuxDirect.print();
    }
}

Test.get();

 12、桥接模式

定义:将抽象部份与它们的具体实现部份分离,使它们都可以独立的变化

补充:通过组合的方式建立两个类之间联系,而不是继承

类型:结构型

适用场景:1、抽象与具体实现之间增加更多的灵活性;2、一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立进行扩展;3、不希望使用继承,或因为多层继承导致系统类的个数剧增;

优点:1、分离抽象部份及具体实现部分;2、提高了系统的可扩展性;3、符合开闭原则;4、符合合成利用原则;

缺点:1、增加了系统的理解与设计难度;2、需要正确的识别出系统中两个独立变化的维度;

相关设计模式:1、桥接模式与组合模式:组合模式更强调的是部份与整体间的组合,而桥接模式强调的是平行级别上不同类的组合;2、桥接模式与适配器模式:共同点是为了让两个功能配合工作,适配器模式是改变已有接口,让共更好的配合,而桥接模式是分离抽象与具体实现分离开使层次结构结合起来;

 创建账户的接口及实现的类

export interface IAccount {
    getAccount(): IAccount
    checkAccount(): void
}

/**
 * 定期账户
 */
export class FixedAccount implements IAccount {
    public getAccount(): IAccount {
        console.log('获取了定期的账户');
        return this;
    }
    public checkAccount(): void {
        console.log('查询了定期的账户');
    }
}

/**
 * 活期账户
 */
export class AliveAccount implements IAccount{
    public getAccount(): IAccount {
        console.log('获取了活期账户的账号');
        return this;
    }

    public checkAccount(): void {
        console.log('查询了活期的账户');
    }
}

创建银行

import {IAccount} from "./Account";

export abstract class Bank {
    private account: IAccount;
    private name: string;
    public constructor(account: IAccount, name: string) {
        this.account = account;
        this.name = name;
    }
    public getAccount(): IAccount {
        console.log(`这个是${this.name}`);
        return this.account.getAccount()
    }
    public checkAccount(): void {
        this.account.checkAccount();
    }
}

/**
 * 创建工商银行
 */
export class ICBCBank extends Bank {
    public constructor(account: IAccount) {
        super(account, '工商银行');
    }
}

/**
 * 创建农业银行
 */
export class ABCBank extends Bank {
    public constructor(account: IAccount) {
        super(account, '农业银行');
    }
}

调用

import {ABCBank, ICBCBank} from "./tools/Bank";
import {AliveAccount, FixedAccount} from "./tools/Account";

export class Test {
    public static t(): void {
        let fixICBCBank = new ICBCBank(new FixedAccount());
        fixICBCBank.getAccount();
        fixICBCBank.checkAccount();

        let liveICBCBank = new ICBCBank(new AliveAccount());
        liveICBCBank.getAccount();
        liveICBCBank.checkAccount();

        let fixABCBank = new ABCBank(new FixedAccount());
        fixABCBank.getAccount();
        fixABCBank.checkAccount();

        let liveABCBank = new ABCBank(new AliveAccount());
        liveABCBank.getAccount();
        liveABCBank.checkAccount()
    }
}

Test.t();

 13、代理模式

 定义:为其他对象提供一种代理,以控制对这个对象的访问

 补充:代理对象在客户端和目标对象之间起到中介的作用

 类型:结构型

 适用场景:1、保护目标对象;2、增强目标对象

 优点:1、代理模式能将代理与真实被调用的目标对象分离;2、一定程度上降低了系统的耦合性,扩展性好;3、保护目标对象;

 缺点:1、代理模式会造成系统设计中类的数目增加;2、在客户端和目标对象增加了一个代理对象,会造成请求速度变慢;3、增加了系统的复杂度

原文地址:https://www.cnblogs.com/rickyctbu/p/11216362.html