规范代码:设计原则与设计模式

142 阅读22分钟

无论是设计模式还是设计原则,都是针对面向对象编程。

一、设计原则(SOLID)

我们通过SOLID的设计原则去优化代码,下面我们举个🌰🌰🌰 去讲解如何在实际项目中去实施。

1、Single Responsibility

单一责任:每一个类(组件)只负责做一件事

Every class should have only one responsibility

错误示例: 用产品等级 去筛选产品列表
import { useEffect, useMemo, useState } from "react";

// 产品的类型,name,rate........
type ProductType = {
  name: string;
  rate: number;
};

export default function ProductPage() {
  const [products, setProducts] = useState<ProductType[]>([]); // 产品的数据
  const [filterRate, setFilterRate] = useState(1); // 过滤产品的🌟🌟等级数

  const fetchProducts = async () => {
    // 发一个请求,然后setProducts一下
    const productsData = [
      {
        name: "产品1",
        rate: 5,
      },
      {
        name: "产品2",
        rate: 4,
      },
    ];
    setProducts(productsData);
  };

  // 获取所有的产品
  useEffect(() => {
    fetchProducts();
  }, []);

  // 设置过滤的等级
  const handleRating = (rate: number) => {
    setFilterRate(rate);
  };

  // 过滤出来的产品
  const filterProducts = useMemo(
    () => products.filter((product) => product.rate >= filterRate),
    [products, filterRate]
  );

  return (
    <div>
      {
        <div>
          这里是一个选择星星等级的dom,有个点击方法,调handleRating,选择过滤的等级
          这里有三百行代码
        </div>        
      }
      {filterProducts.map((product) => {
        return <div>这里有三百行代码来渲染产品相关的内容</div>;
      })}
    </div>
  );
}

这种页面在我们数据平台很正常很普遍,我们一开始都是这么去写代码,请求数据,渲染内容,逻辑也不复杂。

但是从SOLID的角度他违反了单一职责的原则,我们来分析一下。

这个页面做了那些事情:

1、数据的请求,涉及到了一个useEffect,一个function,一个useState;

2、数据的过滤,涉及到了一个useState,一个function,一个useMemo;

3、dom的渲染,需要把这两部分拆成更小的component;

ClearCode

首先,把dom部分拆分成两个更小的组件

interface IFilterProps {
  filterRate: number;
  handleRating: (rate: number) => void;
}
// 这里是选择等级的组件,props接收两个参数,分别是要过滤的等级和选择等级的回调
function FilterRate(props: IFilterProps) {
  return (
    <div>
      这里是一个选择星级的dom,有个点击方法,调handleRating,选择过滤的等级
      这里有三百行代码
    </div>
  );
}

export default FilterRate;
Product
interface IProductProps {
  product: {
    rate: number;
    name: string;
  };
}

function Product(props: IProductProps) {
  return <div>这里有三百行代码来渲染产品相关的内容</div>;
}

export default Product;
修改之后ProductPage的render
return (
    <div>
      <FilterRate filterRate={filterRate} handleRating={handleRating} />
      {filterProducts.map((product) => {
        return <Product product={product} />;
      })}
    </div>
  );

其次,数据的请求(一个useEffect,一个function,一个useState)需要一个自定义hook来处理

useData
interface ProductType {
  name: string;
  rate: number;
}
function useData() {
  const [products, setProducts] = useState<ProductType[]>([]);

  const fetchProducts = async () => {
    // 发一个请求,然后setProducts一下
    const productsData = [
      {
        name: "产品1",
        rate: 5,
      },
      {
        name: "产品2",
        rate: 4,
      },
    ];
    setProducts(productsData);
  };

  // 获取所有的产品
  useEffect(() => {
    fetchProducts();
  }, []);

  return { products };
}

export default useData;
现在的ProductPage
import { useMemo, useState } from "react";
import FilterRate from "./components/filterRate";
import Product from "./components/product";
import useData from "./hook/useData";

export default function ProductPage() {
  const { products } = useData(); // custom hook
  const [filterRate, setFilterRate] = useState(1); // 过滤产品的🌟🌟级数
  // 设置过滤的星级
  const handleRating = (rate: number) => {
    setFilterRate(rate);
    // 其他逻辑
  };

  // 过滤出来的产品
  const filterProducts = useMemo(
    () => products.filter((product) => product.rate >= filterRate),
    [products, filterRate]
  );

  return (
    <div>
      <FilterRate filterRate={filterRate} handleRating={handleRating} />
      {filterProducts.map((product) => {
        return <Product product={product} />;
      })}
    </div>
  );
}

数据的过滤也需要一个custom hook

useRateFilter
function useRateFilter() {
  const [filterRate, setFilterRate] = useState(1); // 过滤产品的🌟🌟级数
  // 设置过滤的星级
  const handleRating = (rate: number) => {
    setFilterRate(rate);
    // 其他逻辑
  };
  return { filterRate, handleRating };
}

export default useRateFilter;

现在的ProductPage

import { useMemo } from "react";
import FilterRate from "./components/filterRate";
import Product from "./components/product";
import useData from "./hook/useData";
import useRateFilter from "./hook/useRateFilter";

export default function ProductPage() {
  const { products } = useData(); // custom hook
  const { filterRate, handleRating } = useRateFilter();

  // 过滤出来的产品
  const filterProducts = useMemo(
    () => products.filter((product) => product.rate >= filterRate),
    [products, filterRate]
  );

  return (
    <div>
      <FilterRate filterRate={filterRate} handleRating={handleRating} />
      {filterProducts.map((product) => {
        return <Product product={product} />;
      })}
    </div>
  );
}

其实就是一个过滤方法,可以把它写在FilterRate组件里,也可以写在Product组件里,也可以定义个utils文件夹,在里面写。这里选择在Product组件里写。

Products
interface IProduct {
  rate: number;
  name: string;
}

interface IProductsProps  {
    products: IProduct[];
    filterRate: number;
}
interface IProductProps {
  product: IProduct;
}

export function filterUitls (props: IProductsProps) {
    const { product,filterRate } = props
    return  products.filter((product) => product.rate >= filterRate);
}

export const Product = (props: IProductProps) => {
  return <div>这里有三百行代码来渲染产品相关的内容</div>;
};

export const Products = (props: IProductsProps) => {

  const filterproducts = useMemo(() => filterUitls(props), [props]) 
  
  return {filterproducts.map((product) => {
        return <Product product={product} />;
      })}
};
clearCode之后的代码
import FilterRate from "./components/filterRate";
import { Products, filterProducts } from "./components/product";
import useData from "./hook/useData";
import useRateFilter from "./hook/useRateFilter";

export default function ProductPage() {
  const { products } = useData();
  const { filterRate, handleRating } = useRateFilter();
  return (
    <>
      <FilterRate filterRate={filterRate} handleRating={handleRating} />
      <Products  product={products} filterRate={filterRate} >
    </>
  );
}

当然 我们最好可以把逻辑和UI 分开

/hook/index.ts
function useProductPage() {
 const { products } = useData();
 const { filterRate, handleRating } = useRateFilter();
 return {
     products,
     filterRate,
     handleRating 
 }
}
最后clearCode之后的代码
import FilterRate from "./components/filterRate";
import { Products, filterProducts } from "./components/product";
import useProductPage from "./hook";

export default function ProductPage() {
  const { products, filterRate, handleRating } = useProductPage();
  return (
    <div>
      <FilterRate filterRate={filterRate} handleRating={handleRating} />
      <Products  product={products} filterRate={filterRate} >
    </div>
  );
}
game最佳实战

2、Open Closed

开闭原则:软件实体应该是可拓展的而不是可修改的 software entities should be open for extension but closed for modification"

错误示例

type ButtonProps = {
  role: "forward" | "back";
};
export default function Button(props: ButtonProps) {
  const { role } = props;
  return (
    <div>
      <button>这是一个按钮</button>
      {role === "forward" && <div>button按钮后边跟一个前进的箭头</div>}
      {role === "back" && <div>button按钮后边跟一个后退的箭头</div>}
    </div>
  );
}

我们开发一个Button组件,按钮后边需要跟一个图标,一开始计划是有前进图标或后退图标。

但是从SOLID的角度他违反了开闭原则

1.如果需要的不是前进后退的箭头,需要的是上箭头或者下箭头,那就要修改源代码。

ClearCode
type ButtonProps = {
  icon?: React.ReactNode;
};
export default function Index(props: ButtonProps) {
  const { icon } = props;
  return (
    <div>
      <button>这是一个按钮</button>
      {icon}
    </div>
  );
}

直接传一个ReactNode,这样直接传一个icon即可。icon可以是任何图标,即满足了可拓展,又不需要修改源代码。

常见的如antd的Button组件的icon属性

3、Liskov Substitution

里氏替换****原则:子类型对象应该可以替代超类型对象

subtype objects should be substitutable for supertype objects

错误示例

interface IInputProps {
  placeholder?: string;
  isBold?: boolean;
}
// 封装一个原生input组件,有个额外的参数isBold来控制字体是否加粗
export default function Input(props: IInputProps) {
  const { isBold, placeholder = "这是封装的input组件" } = props;
  return (
    <div>
      <input
        type="text"
        placeholder={placeholder}
        style={{ fontWeight: isBold ? "bold" : "normal" }}
      />
    </div>
  );
}

没有满足里氏替换原则,子类对象(这个Index组件)不可以替代超类对象(原生input)。换句话说,封装的这个组件只替换了input的placeholder属性。再简单点说,封装的这个组件只能改个placeholder。

ClearCode

1.首先,要继承一下超类的属性

interface IInputProps extends React.InputHTMLAttributes<HTMLInputElement> {
  isBold?: boolean;
}

2.其次,解构剩余props,替换超类对象

interface IInputProps extends React.InputHTMLAttributes<HTMLInputElement> {
  isBold?: boolean;
}
export default function Index(props: IInputProps) {
  const { isBold, placeholder = "这是封装的input组件", ...restProps } = props;
  return (
    <div>
      <input
        type="text"
        placeholder={placeholder}
        style={{ fontWeight: isBold ? "bold" : "normal" }}
        {...restProps}
      />
    </div>
  );
}

4、Interface Segregation

接口分离原则:客户端不需要依赖于它不使用的接口(组件不应依赖于它用不到的props) clients should not depend upon interfaces that they don’t use

错误示例

让我们回到Single Responsibility中的产品组件

import Image from "./Image";
interface IProduct {
  rate: number;
  name: string;
  imageUrl: string;
}
export interface IProductProps {
  product: IProduct;
}
export const Product = (props: IProductProps) => {
  const { product } = props;
  return (
    <div>
      {/* 这里是个产品缩略图 */}
      <Image product={product} />
      这里有三百行代码来渲染产品相关的内容
    </div>
  );
};

不符合接口分离原则 Image只需要产品里面的imageUrl属性,但他拿到了所有的product 的属性

ClearCode
<Image imageUrl={product.imageUrl} />


interface ImageProps {
  imageUrl: string;
}

function Image(props: ImageProps) {
  return (
    <div>
      <img src={props.imageUrl} alt="" />
    </div>
  );
}

export default Image;

5、Dependency Inversion

依赖倒转原则:一个实体应该依赖于抽象而不是具体实现(组件应该更独立,更拓展,而不是为了具体的实现而重写) One Entity should depend upon abstractions not concretions

错误示例

import React, { useState } from "react";

function IForm() {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    // 发起登陆请求
    await fetch("http://请求地址", {
      body: JSON.stringify({
        email,
        password,
      }),
    });
  };

  return (
    <div>
      <form action="" onSubmit={handleSubmit}>
        <input
          type="email"
          name="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
        />
        <input
          type="password"
          name="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
        />
      </form>
    </div>
  );
}

export default IForm;

一个正常的登陆页面,但是如果要复用的话就会有问题,复用的登陆地址不是同一个。

因此他违背了依赖倒转原则:这个组件是为了具体实现而开发的。

具体实现的是———在这个请求地址登陆的。复用的时候需要另一个地址,难道在组件里写if-else吗,肯定不行的。那可以把地址传进来呀,但是不能保证在请求又有其它的逻辑。

ClearCode

import React, { useState } from "react";

function IForm(props: { onSubmit: (email: string, password: string) => void }) {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");

  const { onSubmit } = props;

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    onSubmit(email, password);
  };

  return (
    <div>
      <form action="" onSubmit={handleSubmit}>
        <input
          type="email"
          name="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
        />
        <input
          type="password"
          name="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
        />
      </form>
    </div>
  );
}

export default IForm

这样就可以在调用这个组件的时候通过onSubmit来进行不同的登陆接口的处理。 这样就可以说,这个组件的登陆实现依赖于onSubmit的回调(是抽象的),而不是具体在组件内部实现(具体的登陆接口写在组件内)。简单点说,就是不把登陆接口写死在组件内。

二、设计模式

Design Patterns:elements of reusable object-oriented software 。

书籍推荐:

book.douban.com/subject/143…

book.douban.com/subject/263…

book.douban.com/subject/265…

什么是设计模式

设计模式的定义是由一个建筑模式的概念引出的:

和建筑模式类似,面向对象设计模式,是为我们日常开发中对重复发生的问题的归纳总结,得出来的一套主要解决方案。

分类

设计模式分为三类,它们分别是

  • Creational(创建)

    • factory method 子类决定实例化哪个类,或者根据参数实例化不同对象;
    • prototype 通过复制一个原型创建新对象;
    • abstract factory生成工厂的工厂,其方法返回一个工厂类,可以在实现时返回具体的对象;
    • builder 将对象初始化和后续过程分开,初始化和不同的后续过程组合可以创建出不同对象;
    • singleton 全局只有一个实例;
  • Structural(结构)

    • adapter 让两个不兼容接口一起使用 ,比如target调用request方法,Adaptee只有specialRequest方法,需要提供一个Adapter提供一个request方法帮助调用specialRequest;
    • proxy 两者接口一样,引入中间者而不直接操作原对象;
    • bridge 将接口和实现分离,将接口中对实现的约束指向另一个接口;
    • decorator 和bridge类似,但只是对当前已有功能的增强,;
    • composite 结构类似一棵树,可以递归调用每个结点;
    • facade 为一个系统提供统一的接口;
    • flyweight 利用缓存实现对象共享;
  • Behavioral(行为)

    • chain of responsibility 将处理对象组成一个链表,如果当前对象可以处理则处理否则传给下一个;
    • memento 备忘录
    • interpreter 解释器
    • iterator 迭代器
    • mediator 用一个对象封装多个对象的交互,实现低耦合
    • memento 在外部保存内部状态,比如备份
    • observer 一对多的依赖关系,当被依赖对象变化时,其他都会被通知
    • state 对象的行为根据状态改变而改变
    • strategy 封装一系列可以互相替换的策略
    • template Method 抽象方法中包含默认步骤和默认方法,具体类实现时可以修改某些步骤
    • visitor 观察者

Creational Patterns(创建模式)

用于对象创建,并将创建的逻辑进行封装,对外界只暴露必要的api,从而增加具体实现的灵活性。

1、factory method (工厂方法)

描述:

Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

解释:定义一个类Creator包含一个抽象工厂方法,其子类来决定实例化哪个类。 实现: Product 定义工厂方法创建的对象接口 ConcreteProduct 实现Product的接口 Creator 声明工厂方法 ConcreteCreator 实现工厂方法,用来实例化ConcreteProduct 1)先按上面的结构进行实现


// 抽象类,多态
abstract class Product {
  abstract say():void;
}
// 创建产品实例
class ConcreteProduct extends Product {
  say() {
    console.log("emmmm");
  }
}
// 抽象类,多态
abstract class Creator {
  abstract factorMethod():Product;
  someOperation() {
    const product = this.factorMethod();
    product.say();
  }
}
// 创建工厂
class ConcreteCreator extends Creator {
  factorMethod() {
    return new ConcreteProduct();
  }
}

// 使用
new ConcreteCreator().someOperation()

 // arco BaseNotice 基础组件

interface BaseNoticeState {
  notices: { [key: string]: any }[];
  position?: string;
}

class BaseNotice extends Component<any, BaseNoticeState> {
  constructor(props) {
    super(props);

    this.state = {
      notices: [],
      position: 'topRight',
    };
    this.remove = this.remove.bind(this);
  }

  add = (noticeProps) => {
    const id: string = getId(noticeProps);

    this.setState((prevState) => {
      const oldNotices = prevState.notices;

      // update notice
      if (noticeProps.id && ~oldNotices.findIndex((notice) => notice.id === noticeProps.id)) {
        this.update(noticeProps);
        return prevState;
      }

      return {
        notices: oldNotices.concat({
          ...noticeProps,
          id,
        }),
        position: noticeProps.position,
      };
    });
    return id;
  };

  update = (newNotice) => {
    const updatedNotices = this.state.notices.map((oldNotice) => {
      if (newNotice.id === oldNotice.id) {
        newNotice.update = true;
        return newNotice;
      }
      return oldNotice;
    });
    this.setState(
      {
        notices: updatedNotices,
      },
      () => {
        const notices = this.state.notices.map((oldNotice) => {
          if (newNotice.id === oldNotice.id && oldNotice.update) {
            delete oldNotice.update;
          }
          return oldNotice;
        });
        this.setState({ notices });
      }
    );
  };

  remove(id: string) {
    const newNotices = this.state.notices.filter((notice) => notice.id !== id);
    this.setState({
      notices: newNotices,
    });
  }
...
  clear = () => {
    this.setState({
      notices: [],
    });
  };
}

export default BaseNotice;

// arco Message
class Message extends BaseNotification {
  static useMessage: (config?: ConfigProps) => [messageFuncType, JSX.Element];

  static success: (config: MessageProps | string) => MessageType;

  static info: (config: MessageProps | string) => MessageType;

  static warning: (config: MessageProps | string) => MessageType;

  static error: (config: MessageProps | string) => MessageType;

  static loading: (config: MessageProps | string) => MessageType;

  static normal: (config: MessageProps | string) => MessageType;

  static config = (options: ConfigProps = {}): void => {
    if (options.maxCount) {
      maxCount = options.maxCount;
    }
    if (options.prefixCls) {
      prefixCls = options.prefixCls;
    }
    if (options.duration) {
      duration = options.duration;
    }
    if (options.rtl) {
      rtl = options.rtl;
    }
    if (options.getContainer && options.getContainer() !== container) {
      container = options.getContainer();
      Object.values(messageInstance).forEach(({ instance }) => instance?.clear());
      messageInstance = {};
    }
  };

  static clear: () => void = () => {
    Object.values(messageInstance).forEach(({ instance }) => {
      instance?.clear();
    });
  };

  static addInstance = addInstance;

  ...

  render() {
    const {
      transitionClassNames,
      transitionTimeout: _transitionTimeout,
      prefixCls: _prefixCls,
      rtl: _rtl,
    } = this.props;
    const { notices, position } = this.state;
    const mergedPrefixCls = _prefixCls || prefixCls;
    const mergedRtl = !isUndefined(_rtl) ? _rtl : rtl;
    const prefixClsMessage = mergedPrefixCls ? `${mergedPrefixCls}-message` : 'arco-message';
    const transitionTimeout = {
      enter: isNumber(_transitionTimeout?.enter) ? _transitionTimeout?.enter : 100,
      exit: isNumber(_transitionTimeout?.exit) ? _transitionTimeout?.exit : 300,
    };

    const classNames = cs(`${prefixClsMessage}-wrapper`, `${prefixClsMessage}-wrapper-${position}`);
    return (
      <div className={classNames}>
        <TransitionGroup component={null}>
          {notices.map((notice) => (
            <CSSTransition
              key={notice.id}
              timeout={transitionTimeout}
              classNames={transitionClassNames || `fadeMessage`}
              onExit={(e) => {
                e.style.height = `${e.scrollHeight}px`;
              }}
              onExiting={(e) => {
                e.style.height = 0;
              }}
              onExited={(e) => {
                e.style.height = 0;
                notice.onClose && notice.onClose();
              }}
            >
              <Notice
                {...notice}
                prefixCls={prefixClsMessage}
                iconPrefix={mergedPrefixCls}
                onClose={this.remove}
                noticeType="message"
                rtl={mergedRtl}
              />
            </CSSTransition>
          ))}
        </TransitionGroup>
      </div>
    );
  }
}
// arco Notification
class Notification extends BaseNotification {
  static useNotification: (config?: ConfigProps) => [notificationFuncType, JSX.Element];

  static success: (config: NotificationProps) => ReactInstance;

  ...
  static addInstance: (config: NotificationProps) => ReactInstance = (
    noticeProps: NotificationProps
  ) => {
    let position = noticeProps.position;
    if (isUndefined(noticeProps.position)) {
      position = rtl ? 'topLeft' : 'topRight';
    }
    const _noticeProps = {
      duration,
      ...noticeProps,
    };
    const { instance, pending } = notificationInstance[position] || {};
    if (instance || pending) {
      const add = () => {
        const { instance } = notificationInstance[position] || {};
        const notices = instance.state.notices;
        if (notices.length >= maxCount) {
          const updated = notices[0];
          notices.shift();
          instance.add({
            ..._noticeProps,
            id: updated.id,
          });
        } else {
          instance.add(_noticeProps);
        }
        return instance;
      };

      if (instance) {
        add();
      } else if (pending?.then) {
        pending.then(() => {
          add();
          notificationInstance[position].pending = null;
        });
      }
      return instance;
    }
    const div = document.createElement('div');

    (container || document.body).appendChild(div);
    notificationInstance[position] = {};
    notificationInstance[position].pending = new Promise((resolve) => {
      ReactDOMRender(
        <Notification
          ref={(instance) => {
            if (!notificationInstance[position]) {
              // getContainer 变化时,会重置 notificationInstance
              // pending 中的逻辑执行晚于重置逻辑时,这里需判空
              notificationInstance[position] = {};
            }
            notificationInstance[position].instance = instance;
            instance.add(_noticeProps);
            resolve(null);
            return instance;
          }}
        />,
        div
      );
    });
    return notificationInstance[position].instance;
  };

  render() {
    const { notices } = this.state;
    const { prefixCls: _prefixCls, rtl: _rtl, getContainer } = this.props;
    let position = this.state.position;
    const mergedRtl = !isUndefined(_rtl) ? _rtl : rtl;
    if (isUndefined(position)) {
      position = mergedRtl ? 'topLeft' : 'topRight';
    }
    const mergedPrefixCls = _prefixCls || prefixCls;
    const prefixClsNotification = mergedPrefixCls
      ? `${mergedPrefixCls}-notification`
      : 'arco-notification';
    let transitionClass: string;
    if (position === 'topLeft' || position === 'bottomLeft') {
      transitionClass = 'slideNoticeLeft';
    } else {
      transitionClass = 'slideNoticeRight';
    }
    const classNames = cs(
      `${prefixClsNotification}-wrapper`,
      `${prefixClsNotification}-wrapper-${position}`,
      { [`${prefixClsNotification}-wrapper-rtl`]: rtl }
    );

    const container = getContainer?.();

    const dom = (
      <div className={classNames}>
        <TransitionGroup component={null}>
          {notices.map((notice) => (
            <CSSTransition
              key={notice.id}
              timeout={{
                enter: 400,
                exit: 300,
              }}
              classNames={transitionClass}
              onExit={(e) => {
                e.style.height = `${e.scrollHeight}px`;
              }}
              onExiting={(e) => {
                e.style.height = 0;
              }}
              onExited={(e) => {
                e.style.height = 0;
                notice.onClose && notice.onClose();
              }}
            >
              <Notice
                {...notice}
                onClose={this.remove}
                prefixCls={prefixClsNotification}
                iconPrefix={mergedPrefixCls}
                noticeType="notification"
                rtl={mergedRtl}
              />
            </CSSTransition>
          ))}
        </TransitionGroup>
      </div>
    );

    return container ? createPortal(dom, container) : dom;
  }
}
// 模板、状态、装饰器、工厂

2、prototype(原型)

  1. 描述:

Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.

  1. 解释:指定一个实例对象做原型,然后通过复制这个对象创建一个新对象。 关于这个模式不同语言有不同的实现方法,书上的实现方式是基于c++这种类不是对象的前提下进行的,在ts中,基于原型的继承机制天生的就有prototype,因此可以直接使用。
  2. 实现,借助Object.create()方法可以直接指定一个原型,并可以通过参数对原型的属性进行重写或者添加。注意如果没对原型上的属性进行重写,当原型上的属性变化时会影响继承对象上的属性。
var vehiclePrototype = {
  name: "aaaaa",
  getName: function () {
    console.log("name是:" + this.name);
  },
};

var vehicle = Object.create(vehiclePrototype, {
  name: {
    value: "bbbbb",
  },
});
console.log(vehicle.getName());
console.log(vehiclePrototype.getName());

3、abstract factory(抽象工厂)

描述:

Provide an interface for creating families of related or dependent objects without specifying their concrete classes.

解释:提供一个创建相似或相互依赖对象的接口,即生产一系列工厂,用于生产多个系列的对象。 结构如下:

  1. 实现: AbstractFactory是个抽象类,包含两个抽象方法。 ConcreFactory即工厂方法模式中的ConcreteCreator,其包含工厂方法 AbtractProduct即工厂方法模式中的Product,具体实现如下
//生成用于实例化的class
interface AbstractProductA {
  usefulFunctionA(): string;
}

class ConcreteProductA1 implements AbstractProductA {
  public usefulFunctionA(): string {
    return "The result of the product A1.";
  }
}
class ConcreteProductA2 implements AbstractProductA {
  public usefulFunctionA(): string {
    return "The result of the product A2.";
  }
}
interface AbstractProductB {
  usefulFunctionB(): string;
}
class ConcreteProductB1 implements AbstractProductB {
  public usefulFunctionB(): string {
    return "The result of the product B1.";
  }
}
class ConcreteProductB2 implements AbstractProductB {
  public usefulFunctionB(): string {
    return "The result of the product B2.";
  }
}
//生成工厂的工厂
interface AbstractFactory {
  createProductA(): AbstractProductA;
  createProductB(): AbstractProductB;
}
//生成的工厂1,分别实例化产品系列1
class ConcreteFactory1 implements AbstractFactory {
  public createProductA(): AbstractProductA {
    return new ConcreteProductA1();
  }

  public createProductB(): AbstractProductB {
    return new ConcreteProductB1();
  }
}
//生成的工厂2,分别实例化产品系列2
class ConcreteFactory2 implements AbstractFactory {
  public createProductA(): AbstractProductA {
    return new ConcreteProductA2();
  }

  public createProductB(): AbstractProductB {
    return new ConcreteProductB2();
  }
}
//此时可以分别对两工厂进行实例化后调用工厂函数进行使用,比如
console.log(new ConcreteFactory1().createProductA().usefulFunctionA());

4、builder(建造者)

描述:

Separate the construction of a complex object from its representation so that the same construction process can create different representations.

解释:将一个对象的初始化和后续步骤分开,通过将初始化和不同的后续步骤结合可以创建出不同的结果。 结构如下

实现: Director 构造一个使用buidler接口的对象 Builder 为创建Product对象的各个部分指定抽象接口 ConcreteBuilder 实现builder的接口并提供一个获取实现结果的方法 Product 最终生成的产品


interface Builder {
  buildPart: (brick: number) => void;
}
class ConcreteBuilder implements Builder {
  constructor(public product: Director) {}
  buildPart(brick: number) {
    this.product.partArr.push(brick);
    return this;
  }
  getResult() {
    return this.product.partArr;
  }
}

class Director {
  partArr: number[] = [];
  reset() {
    this.partArr = [];
  }
}

let builder = new ConcreteBuilder(new Director());
console.log(builder.buildPart(1).getResult());
console.log(builder.buildPart(3).getResult());

5、singleton(单例)

描述:

Ensure a class only has one instance, and provide a global point of access to it.

解释:确保一个class只有一个实例提供给全局访问。

"@dp/slardar-logger" slardar监控最佳实践 bytedance.feishu.cn/wiki/wikcnZ…

实现:

/**
 * Creational Patterns(创建模式)
 * 
 * singleton(单例)
 * 
 * 确保一个class只有一个实例提供给全局访问。
 * 
 * "@dp/slardar-logger" slardar监控最佳实践
 * https://bytedance.feishu.cn/wiki/wikcnZNtcwu83tB1WXrOXfeXZkf?theme=DARK&contentTheme=DARK
 */

export class Logger {
  private slardarName: string = 'browserClient';
  private static instance?: Logger;
  private collectEvents: {
    err: IError;
    extra: Record<string, any>;
  }[] = [];

  private constructor() {
    this.init = this.init.bind(this);
    this.info = this.info.bind(this);
    this.warn = this.warn.bind(this);
    this.error = this.error.bind(this);
    this.fatal = this.fatal.bind(this);
  }

  public static get Instance() {
    if (!this.instance) {
      this.instance = new this();
    }
    return this.instance!;
  }
  public sendEvent() {
    if (
      !(window as any)[this.slardarName] ||
      typeof (window as any)[this.slardarName] !== 'function'
    ) {
      return null;
    }
    this.collectEvents.forEach((item) => {
      (window as any)[this.slardarName]?.('captureException', item.err, item.extra);
    });
    this.collectEvents = [];
  }

  /**
   * 自定义日志
   * @param name 日志信息名
   * @param tags 日志详情键值对
   */
  public info(name: string, tags: TagsType = {}) {
    const metrics = pickBy(tags, isNumber);
    const categories = parseTags(tags, true);
    try {
      (window as any)[this.slardarName]('sendEvent', {
        type: 'event',
        name,
        metrics,
        categories,
      });
    } catch (e) {
      console.error('Slardar sendEvent', e);
    }

    // 本地打印日志
    prettyPrint({ label: 'info =>' }, name, metrics, categories);
  }
  /**
   * 上报错误
   * @param level 错误级别, "fatal" | "error" | "warn"
   * @param label 标记 标示自己的业务模块 必填
   * @param describe 自定义信息 必填
   * @param error Error 选填
   * @param tags  额外信息
   */
  public capture(
    level: ErrorLevel,
    label: string,
    describe: string,
    error: IError = new Error(''),
    tags: TagsType = {},
  ) {
    try {
      const detail = errorHandler(level, label, describe, error, tags);
      const { reason, extra } = detail;
      const err = detail.error;
      const reasonStr = reason ? ` (${reason})` : '';
      err.message = `[${detail.label}] - ${detail.describe}${reasonStr}`;
      this.collectEvents.push({ err, extra });
      this.sendEvent();

      // 本地打印日志
      prettyPrint(
        { label: `${level} =>`, color: level2color(detail.level) },
        err.message,
        error,
        tags,
      );
    } catch (e) {
      console.error('Slardar 内部错误', e);
    }
  }
  /**
   * 需要加急到你的错误
   * @param label 标记 标示自己的业务模块 必填
   * @param describe 自定义信息 必填
   * @param error Error 必填
   * @param tags  额外信息
   */
  public fatal = (label: string, describe: string, err?: IError, tags?: TagsType) =>
    this.capture('fatal', label, describe, err, tags);
  /**
   * 普通错误
   * @param label 标记 标示自己的业务模块 必填
   * @param describe 自定义信息 必填
   * @param error Error 必填
   * @param tags  额外信息
   */
  public error = (label: string, describe: string, err?: IError, tags?: TagsType) =>
    this.capture('error', label, describe, err, tags);
  /**
   * 警告
   * @param label 标记 标示自己的业务模块 必填
   * @param describe 自定义信息 必填
   * @param error Error 必填
   * @param tags  额外信息
   */
  public warn: CaptureFunc = (
    label: string,
    describe: string,
    err?: IError,
    tags?: TagsType,
  ) => this.capture('warn', label, describe, err, tags);
}
const logger = Logger.Instance;
export default logger;

/**
 * 全局引用使用
 * import { monitor } from '@dp/slardar-logger';
 * 
    monitor.collect('gamebi_button_click', {
      button_id: 120,
      button_name: '监控',
      is_jumped: 1,
    });
 */

Structural Patterns(结构型模式)

用于组合类与对象

1、adapter(适配器)

描述:

Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.

解释:让两个不兼容的接口可以一起工作

实现: Client 调用request方法 Target 定义了client调用的接口 Adaptee 现存接口,需要适配 Adapter 对Adaptee和接口Target进行适配

/**
 * Structural Patterns(结构型模式
 * 
 * adapter(适配器)
 * 
 * 让两个不兼容的接口可以一起工作
 *  Client 调用request方法, Target 定义了client调用的接口,Adaptee 现存接口,需要适配,Adapter 对Adaptee和接口Target进行适配
 */

interface Target {
  request: () => void;
}

class Client {
  doSomeThing(instance: Target) {
    instance.request();
  }
}

// 旧接口
class OldRequest {
  specialRequest() {
    console.log("special request");
  }
}

class Adapter implements Target {
  request() {
    new OldRequest().specialRequest();
  }
}

// 兼容接口
new Client().doSomeThing(new Adapter());

2、bridge(桥接)

描述:

Decouple an abstraction from its implementation so that the two can vary independently.

解释:将接口和实现分离以便独立变化

实现 常规的接口和实现的是固定的,对应的实现也很难扩展,通过将接口中对实现的约束指向另一个接口,从而将接口和实现分离 Abstraction 抽象类接口,维护一个指向特定接口的引用。其中也可以包含通用的方法 Implementor 定义实现类的接口 ConcreteImplementor 对Implementor接口具体实现 RefineAbsstraction 对Abstraction的实现

/**
 * Structural Patterns(结构型模式
 * 
 *  bridge(桥接)
 * 
 *  
 * 将接口和实现分离以便独立变化
 */

interface Implementor {
  operationImplementation(): string;
}
class ConcretorImplementor implements Implementor {
  operationImplementation() {
    return "Concretor";
  }
}
class Abstraction {
  protected implementation: Implementor;

  constructor(implementation: Implementor) {
    this.implementation = implementation;
  }
  public operation(): string {
    const result = this.implementation.operationImplementation();
    return "default" + result;
  }
}
class RefineAbsstraction extends Abstraction {
  operation() {
    const result = this.implementation.operationImplementation();
    return "refined" + result;
  }
}
console.log(new RefineAbsstraction(new ConcretorImplementor()).operation());

3、composite(组合)

描述:

Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.

解释:将对象组合成“部分-整体”的层次关系,并含有通用接口对外提供。

实现: 结构类似一棵树,调用一个节点的方法,会递归调用所有子结点的对应方法 Component 根节点,声明了所有节点共有的接口和管理子结点的接口,并可以包含默认实现 Composite 根节点的实现 Leaf 叶子节点,只实现了所有节点共有的接口

/**
 * Structural Patterns(结构型模式
 * 
 *  composite (组合)
 * 
 *  
 * 将对象组合成“部分-整体”的层次关系,并含有通用接口对外提供
 * 
 *  结构类似一棵树,调用一个节点的方法,会递归调用所有子结点的对应方法 Component 根节点,声明了所有节点共有的接口和管理子结点的接口,并可以包含默认实现 Composite 根节点的实现 Leaf 叶子节点,只实现了所有节点共有的接口
 */

class Component {
  protected Children: Component[] = [];
  constructor(public id) {}
  add(v: Component) {
    this.Children.push(v);
    return this;
  }
  remove(v: Component) {
    this.Children.splice(
      this.Children.findIndex((item: Component) => item.id === v.id),
      1
    );
    return this;
  }
  getChildren() {
    return this.Children;
  }
  operation() {
    console.log("我是根节点" + this.id);
    this.Children.forEach((item: Component) => item.operation());
  }
}
class Composite extends Component {
  operation() {
    console.log("我一般节点" + this.id);
    this.Children.forEach((item: Component) => item.operation());
  }
}
class Leaf extends Component {
  operation() {
    console.log("我叶节点" + this.id);
  }
}
const root = new Component(1)
  .add(new Composite(2).add(new Leaf(4)))
  .add(new Leaf(3));
root.operation();

4、decorator(装饰器)

描述:

Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.

解释:动态的为对象添加另外的功能,不通过子类来实现。

  1. 实现: Component 定义一个对象接口,可以给这些对象添加功能 ConcreteComponent Component的实现 Decorator 有一个指向修改对象的指针和相同的实现 ConcreteDecoretor 具体进行添加功能操作的class
/**
 * Structural Patterns(结构型模式
 * 
 * decorator(装饰器)
 *
 * 动态的为对象添加另外的功能,不通过子类来实现
 * 
 */

interface Component {
  operation(): string;
}

class ConcreteComponent implements Component {
  operation() {
    return "ConcreteComponent";
  }
}
class Decorator implements Component {
  constructor(protected component: Component) {}
  operation() {
    return this.component.operation();
  }
}
class ConcreteDecoretor extends Decorator {
  operation() {
    return "concrete-" + super.operation();
  }
}
console.log(new ConcreteDecoretor(new ConcreteComponent()).operation());

5、facade(外观)

描述:

Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.

解释:为子系统中的一系列接口提供一个统一的接口 结构如下:

实现: Facade 对外暴露的接口 SubSystem 各个子系

class Facade {
  constructor(private member1: SubSystem1, private member2: SubSystem2) {}
  operation() {
    this.member1.operator1();
    this.member2.operator2();
  }
}
class SubSystem1 {
  operator1() {
    console.log("子系统1工作");
  }
}
class SubSystem2 {
  operator2() {
    console.log("子系统2工作");
  }
}

new Facade(new SubSystem1(), new SubSystem2()).operation();

6、flyweight(共享)

描述:

Use sharing to support large numbers of fine-grained objects efficiently.

解释:利用缓存实现对对象的共享,以满足对大量对象的需要

实现: Flyweight:创建共享对象的class,这里直接实例化进行共享,而不像结构图中用其子类了 FlyweightFactory:生成共享池

class Flyweight {
  constructor(private shareState) {}
  operation(uniqueState) {
    console.log(`共享数据:${this.shareState};非共享数据:${uniqueState}`);
  }
}
class FlyweightFactory {
  private flyweights: { [key: string]: Flyweight } = <any>{}; //共享池
  add(shareState) {
    this.flyweights[JSON.stringify(shareState)] = new Flyweight(shareState); //注意这里由于JSON.stringify造成的对参数的限制
    return this;
  }
  //参数getExistOnly为true只获得当前pool中已经存在的,否则返回为undefined,用于验证缓存是否被启用
  getFlyweight(shareState, getExistOnly: boolean = false) {
    const targetFlyWeight = this.flyweights[JSON.stringify(shareState)];
    if (targetFlyWeight || getExistOnly) {
      return targetFlyWeight;
    }
    const newFlyWeight = new Flyweight(shareState);
    this.flyweights[JSON.stringify(shareState)] = newFlyWeight;
    return newFlyWeight;
  }
}
const flyWeightPool = new FlyweightFactory();
flyWeightPool.add({ a: 1 }).add({ b: 2 });
//注意以下验证严格等于时需要将带daigetExistOnly参数的放在左边,
console.log(
  flyWeightPool.getFlyweight({ a: 1 }, true) ===
    flyWeightPool.getFlyweight({ a: 1 })
); //true
console.log(
  flyWeightPool.getFlyweight({ a: 3 }, true) ===
    flyWeightPool.getFlyweight({ a: 3 })
); //false
console.log(
  flyWeightPool.getFlyweight({ a: 4 }) ===
    flyWeightPool.getFlyweight({ a: 4 }, true)
); //true

7、proxy(代理)

描述:Provide a surrogate or placeholder for another object to control access to it.

解释:对对象的访问进行控制 运行时刻的代理结构

实现: Proxy 实现代理功能的class RealObject 实际访问的对象 Subject 定义代理和实际对象的共有接口

interface Subject {
  request(): string;
}
class RealObject implements Subject {
  request() {
    return "real";
  }
}
//因为在ts中已经存在Proxy关键字,这里以ProxySubject 替代
class ProxySubject implements Subject {
  constructor(private realObject) {}
  request() {
    return "proxy-" + this.realObject.request();
  }
}
console.log(new ProxySubject(new RealObject()).request());

Behavioral Patterns(行为模式)

包含算法和对象间的职责分配。

1、chain of responsibility(职责链)

描述:

Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.

解释:将处理对象连成一条链,前面的对象接收到请求,如果能处理则处理否则传给下一个,直到被处理(或者到最后也没处理),从而将请求方和处理方解耦。 结构如下:

典型的对象结构如下

实现: Handler 处理对象的接口 ConcreteHandler 具体的处理对象

class Handler {
  private nextHandler: Handler;
  setNext(h: Handler) {
    this.nextHandler = h;
    return h;
  }
  //默认处理,如果有下一个则交给下一个处理,否则返回'remnant',可以通过是不是null来判断是不是最后也没处理
  handle(request: number) {
    if (this.nextHandler) {
      return this.nextHandler.handle(request);
    }
    return "remnant";
  }
}

class ConcreteHandler1 extends Handler {
  handle(request: number) {
    if (request === 1) {
      return "done by handle1";
    }
    return super.handle(request);
  }
}
class ConcreteHandler2 extends Handler {
  handle(request: number) {
    if (request === 2) {
      return "done by handle2";
    }
    return super.handle(request);
  }
}
const handler1 = new ConcreteHandler1();
const handler2 = new ConcreteHandler2();
handler1.setNext(handler2);
console.log(handler1.handle(1)); // done by handle1
console.log(handler1.handle(2)); // done by handle2
console.log(handler1.handle(3)); // remnant

2、command(命令)

描述:

Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.

解释:将请求封装成对象,然后以回调的形式调用各种操作。(取消操作暂不考虑)

实现: Command 命令接口 ConcreteCommand 具体的命令,会绑定在对应Receiver上 Receiver 命令的接收者 Invoker 挂载各种命令的对象接口

class Command {
  constructor(protected receiver: Receiver) {
    console.log(receiver);
  }
  execute() {
    console.log("default execute");
  }
}
class Receiver {
  constructor(private name = "") {}
  action() {
    console.log("name:" + this.name);
  }
}
class ConcreteCommand extends Command {
  execute() {
    console.log("concrete execute");
    this.receiver.action();
  }
}
class Invoker {
  private defaultStep = () => {
    console.log("default step");
  };
  onStep1: () => void = this.defaultStep;
  onStep2: () => void = this.defaultStep;
  setStep1(c: Command) {
    this.onStep1 = c.execute.bind(c);//注意这里绑定this,不然执行其中的this指向invoker
  }
  setStep2(c: Command) {
    this.onStep2 = c.execute.bind(c);
  }
}
const invoker = new Invoker();
invoker.setStep1(new ConcreteCommand(new Receiver("xiaoming")));
invoker.onStep1();
invoker.onStep2();

3、interpreter(解释器)

描述:

Given a language, define a represention for its grammar along with an interpreter that uses the representation to interpret sentences in the language.

解释:就是一个指定规则的解释器,也可以简化为基于一种关系的映射。

实现:这里将解释器的作用简化为一种映射,这个模式的重点在于具体解释规则的指定

class Expression {
  interpret(props: string) {
    return props.length;
  }
}
console.log(new Expression().interpret("2222"));

4、iterator(迭代器)

描述:Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.

解释:提供一个顺序访问对象元素的方法,而不暴露底层实现,其实ts在语言层面已经实现得很好了

实现:以下实现的是对数组的迭代 Iterator 迭代器对象接口 ConcretorIterator 具体迭代对象 Aggregator 要迭代的对象接口 ConcreteAggregator 具体要迭代的对象

interface IteratorInterface<T> {
  first(): T;
  next(): T;
  isDone: boolean;
  curItem: T;
}
interface Aggregator {
  createIterator(): IteratorInterface<string>;
}
class ConcreteAggregator implements Aggregator {
  items: string[] = [];
  addItem(i: string) {
    this.items.push(i);
    return this;
  }
  createIterator() {
    return new ConcreteIterator(this);
  }
}
class ConcreteIterator implements IteratorInterface<string> {
  location: number = 0;
  constructor(private collection: ConcreteAggregator) {}
  first() {
    return this.collection.items[0];
  }
  next() {
    const item = this.collection.items[this.location];
    this.location += 1;
    return item;
  }
  get isDone() {
    return this.location >= this.collection.items.length;
  }
  get curItem() {
    return this.collection.items[this.location];
  }
}
const aggregator = new ConcreteAggregator();
aggregator.addItem("first").addItem("second").addItem("third");
const iterator = aggregator.createIterator();
while (!iterator.isDone) {
  console.log(iterator.next());
}

5、mediator(中介)

描述:

Define an object that encapsulates how a set of objects interact. The mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.

解释:用一个对象封装一系列的对象交互,各对象交互低耦合

实现: Mediator 中介接口,定义了一个通信的方法 ConcreteMediator 具体中介 Colleague 一系列需要协同的对象

interface Mediator {
  notify(receiver: string): void;
}
class ConcreteMediator implements Mediator {
  constructor(private c1: Colleague, private c2: Colleague) {
    c1.setMediator(this);
    c2.setMediator(this);
  }
  notify(receiver) {
    this[receiver] && this[receiver].toDo();
  }
}
class Colleague {
  mediator: Mediator;
  setMediator(m: Mediator) {
    this.mediator = m;
  }
  toDo() {}
  toCall(listener: string) {}
}
class ConcreteColleague1 extends Colleague {
  toDo() {
    console.log("对象1被被调用");
  }
  toCall(listener: string) {
    console.log("对象1发起调用");
    this.mediator.notify(listener);
  }
}
class ConcreteColleague2 extends Colleague {
  toDo() {
    console.log("对象2被被调用");
  }
  toCall(listener: string) {
    console.log("对象2发起调用");
    this.mediator.notify(listener);
  }
}
const c1 = new ConcreteColleague1();
const c2 = new ConcreteColleague2();
const m = new ConcreteMediator(c1, c2);
c1.toCall("c2");
c2.toCall("c1");

6、memento(备忘录)

  1. 描述:

Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later.

解释:在不破坏封装性的前提下,在外部保存内部状态 结构如下:

实现: Memento 备忘录 Originator 需要备份的对象class

class Memento {
  private state: number;
  getState() {
    return this.state;
  }
  setState(state) {
    this.state = state;
  }
}
class Originator {
  constructor(private memento: Memento, public state: number) {}
  save() {
    this.memento.setState(this.state);
  }
  chenge() {
    this.state += 1;
  }
  check() {
    return this.memento.getState();
  }
}
const o = new Originator(new Memento(), 0);
console.log(o.state);
o.save();
o.chenge();
console.log(o.state);
console.log(o.check());

7、observer(观察者)

描述:

Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

解释:定义一个一对多的依赖关系,一个对象变化时,所有依赖该对象的对象都会被通知

实现: Observer 观察者接口 Subject 观察者的依赖接口

interface Observer {
  update(state: number): void;
  observerState: number;
}
class ConcreteObserver implements Observer {
  observerState = 0;
  constructor(public id) {}
  update(state) {
    this.observerState = state;
  }
}
class Subject {
  constructor(private state: number) {}
  observers: ConcreteObserver[] = [];
  init() {
    this.notify();
  }
  attach(o: ConcreteObserver) {
    if (!this.observers.find((item) => item.id === o.id)) {
      this.observers.push(o);
    }
    return this;
  }
  detach(o: ConcreteObserver) {
    const index = this.observers.findIndex((item) => (item.id = o.id));
    this.observers.splice(index, 1);
  }
  notify() {
    console.log(this.observers);
    this.observers.forEach((item) => item.update(this.state));
  }
  modifyState() {
    this.state += 1;
    this.notify();
  }
}
const o1 = new ConcreteObserver("a");
const o2 = new ConcreteObserver("b");
const s = new Subject(1);
s.attach(o1).attach(o2).init();
console.log(o1.observerState);
console.log(o2.observerState);
s.modifyState();
console.log(o1.observerState);
console.log(o2.observerState);




indoc sdk, 内部注册事件
 const { useStoryCommand } = indoc;
 
  // 引导第四步保存看板
  useStoryCommand('BY_DIM_LOGIN_COUNT', 'step-save-report', () => {
    // 页面行为
  });

8、state(状态)

  1. 描述:

Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.

  1. 解释:对象的行为根据状态改变而改变
  2. 实现: State 状态接口 ConstrateState 具体状态 Context 维持状态的环境
interface State {
  handler(): void;
}
class ConstrateState1 implements State {
  handler() {
    console.log("state1");
  }
}
class ConstrateState2 implements State {
  handler() {
    console.log("state2");
  }
}

class Context {
  constructor(private state: State) {}
  transitionTo(state: State) {
    this.state = state;
  }
  request() {
    this.state.handler();
  }
}
const s1 = new ConstrateState1();
const s2 = new ConstrateState2();
const context = new Context(s1);
context.request();
context.transitionTo(s2);
context.request();

9、strategy(策略)

  1. 描述:

Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

解释:封装一系列可以互相替换的策略

实现: 状态模式和策略模式的结构图几乎一样,具体实现可以参考状态模式。它们俩的不同至少有两个方面 1)目的不同,状态模式根据状态的切换不同做法,策略模式是存在几种可以替换的策略 2)不同状态是互相依赖的,不同策略没有这种关系。

10、template Method(模板方法)

描述:

Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. The Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.

解释:定义一个算法在操作中的框架,可以在不改变框架的前提下修改特定步骤,可用于创建hook,比如vue或react中特定生命周期钩子函数。

实现: AbstractClass 抽象类中包含默认步骤和模板方法 ConstreteClass 具体类可以修改某些步骤,如果重定义则执行新的,否则执行默认的

abstract class AbstractClass {
  operation1() {
    console.log("default operation1");
  }
  operation2() {
    console.log("default operation1");
  }
  templateMethod() {
    this.operation1();
    this.operation2();
  }
}
class ConstreteClass extends AbstractClass {
  operation1() {
    console.log("constrete operation");
  }
}
new ConstreteClass().templateMethod();

11、visitor(访问者)

描述:

Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.

解释:在不改变被访问对象的前提下,修改该对象内部的操作行为,比如babel中的visitor 。 结构图:

实现: Element 被访问对象接口 ConcreteElement 具体访问的元素 Visitor 访问者接口,包含访问行为 ConcreteVisitor 具体的访问者,包含具体的访问行为

interface Visitor {
  visitA(e: ConcreteElementA): void;
  visitB(e: ConcreteElementB): void;
}
class ConstreteVisitor1 implements Visitor {
  visitA(e) {
    console.log(e);
    console.log(`第一种对A的访问,id为:${e.state}`);
  }
  visitB(e) {
    console.log(`第一种对B的访问,id为:${e.state}`);
  }
}
interface ElementInterface {
  accept(visitor: Visitor): void;
}
class ConcreteElementA implements ElementInterface {
  state = "a";
  accept(visitor: Visitor) {
    visitor.visitA(this);
  }
}
class ConcreteElementB implements ElementInterface {
  state = "b";
  accept(visitor: Visitor) {
    visitor.visitB(this);
  }
}
new ConcreteElementA().accept(new ConstreteVisitor1());
new ConcreteElementB().accept(new ConstreteVisitor1());