百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 优雅编程 > 正文

结合TypeScript前端人用的设计模式

sinye56 2024-10-22 16:26 12 浏览 0 评论

前言

程序编程的历史由来已久,一个优秀的编程思想可以让我们写出更好的逻辑代码。面向对象的程序应该具有可维护性,代码可复用性,扩展性以及灵活性。为了实现以上的功能,大佬们总结出一套可用的内功心法,那就是设计模式。设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式,不仅方便写出更复杂的业务逻辑,更增加了业务代码的可读性,和维护性

设计原则

设计原则是设计模式所遵循的规则,设计模式就是实现了这些原则,从而达到了代码复用、增加可维护性的目的。

单一职责(Single Responsibility Principle - SRP)

一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因。

一个函数,只用来干一件事,承担了过多的责任也就意味着多个功能的耦合。多个函数共同组成复杂的逻辑

开放封闭(Open Closed Principle - OCP)

对外开放,对内封闭

对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对类进行任何修改。

里氏替换(Liskov Substitution Principle - LSP)

子类可以扩展父类的功能,但不能改变父类原有的功能


子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。

  • 子类中可以增加自己特有的方法。
  • 当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
  • 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。

  • 接口隔离(Interface Segregation Principle - ISP)

    建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少

    使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口,根据接口隔离原则,当一个接口太大时,我们需要将它分割成一些更细小的接口,使用该接口的客户端仅需知道与之相关的方法即可,接口隔离根本在于不要强迫客户端程序依赖他们不需要使用的方法



    依赖倒置(Dependence Inversion Principle - DIP)

    依赖倒置原则的核心思想是面向接口编程,不应该面向实现类编程。

    也就是使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明,以及数据类型的转换等,而不是使用具体的类

    Tip: 设计原则简称SOLID原则,方便记忆。同时还有一些不常用的设计原则。如:组合/聚合复用原则,无环依赖原则,共同重用原则

    设计模式的分类

    总体来说设计模式分为三大类

    创建型 (5种)

    • 单例模式(Singleton Pattern)
    • 工厂方法模式(Factory Method Pattern)
    • 抽象工厂模式(Abstract Factory Pattern)
    • 建造者模式(Builder Pattern)
    • 原型模式(Prototype Pattern)


    结构型(7种)

    • 适配器模式(Adapter Pattern)
    • 装饰者模式(Decorator Pattern)
    • 代理模式(Proxy Pattern)
    • 外观模式(Facade Pattern)
    • 桥接模式(Bridge Pattern)
    • 组合模式(Composite Pattern)
    • 享元模式(Flyweight Pattern)

    行为型(11种)

    • 策略模式(Strategy Pattern)
    • 模板方法模式(Template Method Pattern)
    • 观察者模式(Observer Pattern)
    • 迭代器模式(Iterator Pattern)
    • 责任链模式(Chain of Responsibility Pattern)
    • 命令模式(Command Pattern)
    • 备忘录模式(Memento Pattern)
    • 状态模式(State Pattern)
    • 访问者模式(Visitor Pattern)
    • 中介者模式(Mediator Pattern)
    • 解释器模式(Interpreter Pattern)

    Tip:图解



    其实还有两类:并发型模式和线程池模式。用一个图片来整体描述一下:


    设计模式具体说明

    单例模式(Singleton Pattern)

    通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例。单例对象的类只能允许一个实例(instance)存在


    使用场景

    小编之前在项目中,使用element-ui的Message组件,Message默认是可以展示多条的,但是如果客户是个急性子,多点了几次,就会出现满屏的Message,这个时候我们只需要一个Message来通知即可

    大家都知道,在写后端的时候,数据库连接池对象也会被设计成单例模式

    优缺点

    优点:

    1. 在内存中只有一个对象,节省内存空间;

    2. 避免频繁的创建销毁对象,可以提高性能;

    缺点:

    1.不适用于变化频繁的对象;

    单例模式的实现

    饿汉式


    懒汉式


    javascript是单线程运行,所有没有多线程的加锁概念

    js闭包实现单例模式


    实现单一Message


    • 当前以上的方式是为了演示单例模式,并不是最好的处理方法

    工厂方法模式(Factory Method Pattern)

    工厂方法模式,又称工厂模式,通过定义工厂父类负责定义创建对象的公共接口,而子类则负责生成具体的对象。



    由上图可以看出,工厂方法模式需要四个角色


    • 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 newProduct() 来创建产品。
    • 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
    • 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
    • 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。

    使用场景


    建造者模式(Builder Pattern)

    建造者模式又叫创建者模式,是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。创建者模式隐藏了复杂对象的创建过程,它把复杂对象的创建过程加以抽象,通过子类继承或者重载的方式,动态的创建具有复合属性的对象。


    如上图所示,建造者模式一共有4个角色

    • Product: 最终要生成的对象。
    • Builder: 构建者的抽象基类(有时会使用接口代替)。其定义了构建Product的抽象步骤,其实体类需要实现这些步骤。其会包含一个用来返回最终产品的方法Product getProduct()。
    • ConcreteBuilder: Builder的实现类。
    • Director: 决定如何构建最终产品的算法. 其会包含一个负责组装的方法Construct(Builder builder), 在这个方法中通过调用builder的方法,就可以设置builder,等设置完成后,就可以通过builder的 getProduct() 方法获得最终的实例。

    使用场景

    ```

    abstract class PhoneBuilder {

    public abstract setBrand(): void;

    public abstract setSystem(): void;

    public abstract getPhone(): Phone;

    }

    class Phone {

    private brand: string = '';

    private cpu: string = '';

    private screen: number = 0;

    private system: string = '';

    constructor(cpu: string, screen: number) {

    this.cpu = cpu;

    this.screen = screen;

    }

    setBrand(brand: string) {

    this.brand = brand;

    }

    setCPU(cpu: string) {

    this.cpu = cpu;

    }

    setScreen(screen: number) {

    this.screen = screen;

    }

    setSystem(system: string) {

    this.system = system;

    }

    }

    class HuaWei extends PhoneBuilder {

    private phone: Phone;

    constructor(cpu: string, screen: number) {

    super();

    this.phone = new Phone(cpu, screen);

    }

    public setBrand() {

    this.phone.setBrand('huawei');

    }

    public setSystem() {

    this.phone.setSystem('hongmeng');

    }

    public getPhone() {

    return this.phone;

    }

    }

    class PhoneDirector {

    makeComputer(builder: PhoneBuilder){

    builder.setBrand();

    builder.setSystem();

    }

    }

    var director = new PhoneDirector()

    var builder = new HuaWei('八核', 5.8)

    director.makeComputer(builder)

    var huawei = builder.getPhone()

    // Phone { brand: 'huawei', cpu: '八核', screen: 5.8, system: 'hongmeng' }

    ```

    原型模式(Prototype Pattern)

    用于创建重复的对象,同时又能保证性能。提供了一种创建对象的最佳方法。


    适配器模式(Adapter Pattern)

    将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。


    使用场景


    装饰者模式(Decorator Pattern)

    装饰者模式又名包装(Wrapper)模式。装饰者模式动态地将责任附加到对象身上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。


    使用场景

    ```

    interface HuaWei {

    getMemory(): string

    getPrice(): number

    }

    class HuaWeiPro30 implements HuaWei {

    getMemory(){

    return '16G'

    }

    getPrice(){

    return 6888

    }

    }

    class HuaWeiPro40 implements HuaWei {

    getMemory(){

    return '16G'

    }

    getPrice(){

    return 8888

    }

    }

    abstract class ScreenHuaWei implements HuaWei {

    abstract huawei: HuaWei;

    abstract getScreen(): number;

    getPhone(){

    return this.huawei

    }

    setPhone(phone: HuaWei){

    this.huawei = phone

    }

    getMemory(){

    return this.huawei.getMemory()

    }

    getPrice(){

    return this.huawei.getPrice()

    }

    }

    class AddScreen extends ScreenHuaWei {

    huawei: HuaWei;

    constructor(phone:HuaWei){

    super()

    this.huawei = phone

    }

    getScreen(){

    return 5.5

    }

    }

    var phoneWithoutScreen = new HuaWeiPro40()

    var phoneScreen5 = new AddScreen(phoneWithoutScreen)

    console.log(phoneScreen5.getScreen()) // 5.5

    ```

    代理模式(Proxy Pattern)

    可以为其他对象提供一种代理以控制对这个对象的访问。所谓代理,是指具有与被代理对象相同的接口的类,客户端必须通过代理与被代理的目标类进行交互,而代理一般在交互的过程中(交互前后),进行某些特定的处理。


    使用场景


    策略模式(Strategy Pattern)

    策略模式定义了一系列算法,并将每一个算法封装起来,而且使他们还可以相互替换,策略模式让算法独立于使用它的客户而独立变化。


    Tip:

    • Context :用来操作策略的上下文环境
    • Stragety: 策略的抽象
    • ConcreteStragetyA, ConcreteStragetyB 具体策略实现。

    使用场景


    观察者模式(Observer Pattern)

    有时被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。


    使用场景

    ```

    class Subject {

    private state: number;

    private observers: Array<Observer>;

    constructor() {

    this.state = 0;

    this.observers = [];

    }

    getState() {

    return this.state;

    }

    setState(state: number) {

    this.state = state;

    this.notifyAllObservers();

    }

    notifyAllObservers() {

    this.observers.forEach((observer) => {

    observer.update();

    });

    }

    attach(observer: Observer) {

    this.observers.push(observer);

    }

    }

    //观察者类

    class Observer {

    name: string;

    subject: Subject;

    constructor(name: string, subject: Subject) {

    this.name = name;

    this.subject = subject;

    this.subject.attach(this); //把观察者添加到主题中观察者列表上来

    }

    update() {

    console.log(`${this.name} update, state: ${this.subject.getState()}`);

    }

    }

    let sub = new Subject();

    let obs1 = new Observer('obs1', sub);

    let obs2 = new Observer('obs2', sub);

    let obs3 = new Observer('obs3', sub);

    sub.setState(1);

    sub.setState(2);

    sub.setState(3);

    ```

    状态模式(State Pattern)

    状态(State)模式,当一个对象的内在状态改变时允许改变其行为,这个对象看起来就像是改变了其类。状态模式主要解决的是当控制一个对象状态转换条件表示式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。


    使用场景

    ```

    abstract class State {

    protected context: Context | undefined;

    setContext(context: Context): void {

    this.context = context;

    }

    public abstract handle1(): void;

    }

    class ConcreteState1 extends State {

    handle1(): void {

    //...

    console.log('ConcreteState1 的 handle1 方法');

    }

    }

    class Context {

    static STATE1: State = new ConcreteState1();

    currentState: State | undefined;

    getCurrentState(): State | undefined {

    return this.currentState;

    }

    setCurrentState(currentState: State) {

    this.currentState = currentState;

    this.currentState.setContext(this);

    }

    handle1(): void {

    this.currentState && this.currentState.handle1();

    }

    }

    let context = new Context();

    context.setCurrentState(new ConcreteState1());

    context.handle1();

    ```

    结束语

    以上介绍了几种常用的设计模式,在javascript中,设计模式用的不多,但是要写出来很需要一份内功力,需要能帮助到小伙伴们~

    相关推荐

    程序员:JDK的安装与配置(完整版)_jdk的安装方法

    对于Java程序员来说,jdk是必不陌生的一个词。但怎么安装配置jdk,对新手来说确实头疼的一件事情。我这里以jdk10为例,详细的说明讲解了jdk的安装和配置,如果有不明白的小伙伴可以评论区留言哦下...

    Linux中安装jdk并配置环境变量_linux jdk安装教程及环境变量配置

    一、通过连接工具登录到Linux(我这里使用的Centos7.6版本)服务器连接工具有很多我就不一一介绍了今天使用比较常用的XShell工具登录成功如下:二、上传jdk安装包到Linux服务器jdk...

    麒麟系统安装JAVA JDK教程_麒麟系统配置jdk

    检查检查系统是否自带java在麒麟系统桌面空白处,右键“在终端打开”,打开shell对话框输入:java–version查看是否自带java及版本如图所示,系统自带OpenJDK,要先卸载自带JDK...

    学习笔记-Linux JDK - 安装&amp;配置

    前提条件#检查是否存在JDKrpm-qa|grepjava#删除现存JDKyum-yremovejava*安装OracleJDK不分系统#进入安装文件目...

    Linux新手入门系列:Linux下jdk安装配置

    本系列文章是把作者刚接触和学习Linux时候的实操记录分享出来,内容主要包括Linux入门的一些理论概念知识、Web程序、mysql数据库的简单安装部署,希望能够帮到一些初学者,少走一些弯路。注意:L...

    测试员必备:Linux下安装JDK 1.8你必须知道的那些事

    1.简介在Oracle收购Sun后,Java的一系列产品就被整合到Oracle官网中,打开官网乍眼一看也不知道去哪里下载,还得一个一个的摸索尝试,而且网上大多数都是一些Oracle收购Sun前,或者就...

    Linux 下安装JDK17_linux 安装jdk1.8 yum

    一、安装环境操作系统:JDK版本:17二、安装步骤第一步:下载安装包下载Linux环境下的jdk1.8,请去官网(https://www.oracle.com/java/technologies/do...

    在Ubuntu系统中安装JDK 17并配置环境变量教程

    在Ubuntu系统上安装JDK17并配置环境变量是Java开发环境搭建的重要步骤。JDK17是Oracle提供的长期支持版本,广泛用于开发Java应用程序。以下是详细的步骤,帮助你在Ubuntu系...

    如何在 Linux 上安装 Java_linux安装java的步骤

    在桌面上拥抱Java应用程序,然后在所有桌面上运行它们。--SethKenlon(作者)无论你运行的是哪种操作系统,通常都有几种安装应用程序的方法。有时你可能会在应用程序商店中找到一个应用程序...

    Windows和Linux环境下的JDK安装教程

    JavaDevelopmentKit(简称JDK),是Java开发的核心工具包,提供了Java应用程序的编译、运行和开发所需的各类工具和类库。它包括了JRE(JavaRuntimeEnviro...

    linux安装jdk_linux安装jdk软连接

    JDK是啥就不用多介绍了哈,外行的人也不会进来看我的博文。依然记得读大学那会,第一次实验课就是在机房安装jdk,编写HelloWorld程序。时光飞逝啊,一下过了十多年了,挣了不少钱,买了跑车,娶了富...

    linux安装jdk,全局配置,不同用户不同jdk

    jdk1.8安装包链接:https://pan.baidu.com/s/14qBrh6ZpLK04QS8ogCepwg提取码:09zs上传文件解压tar-zxvfjdk-8u152-linux-...

    运维大神教你在linux下安装jdk8_linux安装jdk1.7

    1.到官网下载适合自己机器的版本。楼主下载的是jdk-8u66-linux-i586.tar.gzhttp://www.oracle.com/technetwork/java/javase/downl...

    window和linux安装JDK1.8_linux 安装jdk1.8.tar

    Windows安装JDK1.8的步骤:步骤1:下载JDK打开浏览器,找到JDK下载页面https://d.injdk.cn/download/oraclejdk/8在页面中找到并点击“下载...

    最全的linux下安装JavaJDK的教程(图文详解)不会安装你来打我?

    默认已经有了linux服务器,且有root账号首先检查一下是否已经安装过java的jdk任意位置输入命令:whichjava像我这个已经安装过了,就会提示在哪个位置,你的肯定是找不到。一般我们在...

    取消回复欢迎 发表评论: