首页 > 其他分享 >结构型-装饰器模式

结构型-装饰器模式

时间:2023-03-18 16:14:19浏览次数:55  
标签:function run 代码 模式 AOP console 装饰 结构型

定义

  如果希望动态给某个类添加一些属性或者方法,但是你又不希望这个类派生的对象受到影响,那么装饰器模式就可以给你带来这样的体验。

它的定义就是在不改变原对象的基础上,通过对其进行包装拓展,使得原有对象可以动态具有更多功能,从而满足用户的更复杂需求

举个例子,一部手机,你可以买各种花里胡哨的手机壳等,这些手机壳其实就起到了装饰的作用,对手机本身的功能没有影响。

那么装饰器模式的特点就来了:

  1. 不影响原有功能
  2. 可同时装饰多个

js模拟装饰模式

向一个现有对象添加新的功能,同时又不改变其结构。

如我在跑步,但是我想一边跑步一边听歌。我们通常很快速的下写如下代码

/*
 如跑步时我想听音乐
*/
function run() {
  console.log('joel run')
}

// 一般改成如下:
function run() {
  console.log('joel run')
  // 听音乐代码 ...
}
// 或者把听音乐抽成一个方法
function music() {
  console.log('听歌')
}
function run() {
  console.log('joel run')
  music()
}
// 以上都违反来关闭原则

上面的代码都需要在原有的代码上面做调整,这样违反了关闭原则,按照装饰模式的定义,我们可以用装饰器模式扩展我们的跑步方法。

如下把上面的代码改成符合装饰模式思想

/***************************** 1、最简单的装饰 **************************/
// 1.1 入口套一层函数
function decoratorRun (fn) {
  fn()
  music()
}

decoratorRun(run)

// 1.2 中间变量保存引用
function run () {
  console.log('joel run')
}
function music() {
  console.log('听歌')
}
let _run = run;
run = function () {
  _run()
  music()
}
run()
// 上面两种都是固定了调用的位置不太灵活,并没有当成一个工具函数来使用

/***************************** 2、工具函数 **************************/
// 装饰fn函数,利用闭包特点
const decoratorRun1 = function (fn, afterFn) {
  return function (...args) {
    const res = fn.apply(this, args)
    afterFn.apply(this, args );
    return res;
  }
}
function run() {
  console.log('joel run')
}
function music(name) {
  console.log(`我正在听${name},很好听。`)
}
const runAndMusic = decoratorRun1(run,music);
runAndMusic('孤勇者');

/************************* 3、原型链, 把入口挂载到Function原型上*********/
Function.prototype.before = function (beforeFn) {
  const _self = this;
  return function (...args) {
    beforeFn.apply(this, args);
    return _self.apply(this, args);
  }
}

Function.prototype.after = function (afterFn) {
  const _self = this;
  return function (...args) {
    const result = _self.apply(this, args);
    afterFn.apply(this, args);
    return result;
  }
}
function run() {
  console.log('joel run')
}
function music(name) {
  console.log(`我正在听${name},很好听。`)
}
const runAndMusic1 = run.after(music)
runAndMusic1('孤勇者')

/***************************** 4、es7+ 装饰器写法 **************************/
@testable
class MyTestableClass {
  // ...
}

function testable(target) {
  target.isTestable = true;
}

MyTestableClass.isTestable // true

js 中的装饰器

在最新版本的es next 中,引入了 Decorator (装饰器的语法),从语言语法层面帮助我们快速的扩展我们的类(class)的功能。

function testAble(isTestable) {
    return function(target) {
        target.isTestable = isTestable;
    }
}

@testAble(true)
class MyTestClass {}
MyTestClass.isTestable; // true

@testAble(false)
class MyTestClass1 {}
MyTestClass1.isTestable; // false

装饰器可以参考 https://es6.ruanyifeng.com/#docs/decorator

你要知道的AOP概念

在上面模拟装饰器的时候,把函数挂载到Function原型上,有那么一点点AOP的思路。

AOP为Aspect Oriented Programming的缩写,指面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。

AOP 是一种编程范式,通俗的可以理解为这种在运行时,编译时,类和方法加载时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。

如上图从横向角度来分析,业务1-3都需要日志,安全、其他等这种与业务无关的代码逻辑,这个时候我们把这种代码抽离出来,在动态的插入的技术就是面向切面编程。

AOP主要把业务逻辑无关的功能进行抽离,这些与业务逻辑无关的内容如日志打印、数据统计、异常处理、权限控制等各个部分进行隔离,在通过动态注入的方式注入到业务代码中。这样做既保证了业务内部高内聚,模块之间低耦合,方便管理与业务无关的模块,有利于未来的可操作性和可维护性。

面向切面编程(AOP)是一种通过横切关注点(Cross-cutting Concerns)分离来增强代码模块性的方法,它能够在不修改业务主体代码的情况下,对它添加额外的行为。

优点是:这样的做法,对原有代码毫无入侵性

AOP场景

AOP采取横向思考的思维,横向抽取机制,取代了传统纵向继承体系重复性代码的编写方式(例如性能监视、事务管理、安全检查、缓存、日志记录等)。

在软件系统设计的时候,我们需要把一个大的系统按照业务功能进行拆分,做到高内聚、低耦合。

但是呢,拆分之后会产生一些通用性的东西,比如日志,安全,事务,性能统计等,这些非功能性需求。

  1. 记录日志
  2. 监控方法运行时间 (监控性能)
  3. 权限控制
  4. 缓存优化 (第一次调用查询数据库,将查询结果放入内存对象, 第二次调用, 直接从内存对象返回,不需要查询数据库 )
  5. 事务管理 (调用方法前开启事务, 调用方法后提交或者回滚、关闭事务 )

AOP、装饰器模式

 AOP 与装饰器都是解决一样的问题,隔离业务逻辑无关的代码快。

装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为如有日志、性能测试、事务处理等,在 JavaScript 中,我们可以通过装饰者模式来实现 AOP,但是两者并不是一个维度的概念。 AOP 是一种编程范式,而装饰者是一种设计模式。 

小结

  1. 装饰者模式非常适合给业务代码附加非业务相关功能
  2. 装饰者模式非常适合无痛扩展别人的代码
  3. 装饰器模式还真的可以让代码变得优雅

标签:function,run,代码,模式,AOP,console,装饰,结构型
From: https://www.cnblogs.com/longbensong/p/17230968.html

相关文章

  • WPF 在 MVVM 模式下实现窗口后台代码与ViewModel交互
    在WPFMVVM模式中,UI层基本上与ViewModel通过依赖属性和命令绑定交互。有时候互联网上提供的第三方控件不支持绑定,只能在后台代码中赋值和更新,如何在MVVM模式中对这种......
  • 设计模式(二十四)----行为型模式之迭代器模式
    1概述定义:提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。2结构迭代器模式主要包含以下角色:抽象聚合(Aggregate)角色:定义存储、添加、......
  • 设计模式之桥接模式
    前提:含抽象类、扩展接口、具体实现类、具体扩展类。使用组合的方式替代继承。(ps:最近想不到更新啥,就先更着以前呆的桥接模式) 区别:桥接模式与策略模式均采用......
  • 设计模式之一些其他知识
    1组合和继承有何区别?(暂时没想到更啥,先更着之前写的东西吧。。。)-继承:is-a关系。实现对父类的扩展,但是继承的--缺点:父类细节对子类可见;高耦合 -组合:ha......
  • 设计模式之状态模式
    前提:含环境类、状态接口、具体状态类。将每个状态实现为一个对象即行为。(ps:没想到更啥,先更着以前写的状态模式吧。。。。) 1状态接口:publicinterfac......
  • 设计模式之适配器模式
    前提:适配器模式有三种-类、对象、接口适配器。(暂时没想到更啥,先更着以前写的适配器模式吧。。。)使用场景:假设手上有一个ps2插头的设备,但主机对外是usb,这时候需要弄个......
  • 单例代理模式之懒汉模式
    /***@Nyapii*/publicclassSingleTon1{//这是饿汉,我饿怕了,不论在什么情况下,我都要一个属于我的汉堡publicstaticvoidmain(String[]args){......
  • python __new__方法与单例模式
    1、new()至少要有一个参数cls,代表当前类,此参数在实例时由python解释器自动识别,2、new()必须要有返回值,返回实例化出来的实例,这点在自己实现__new__时要特别注意,可以......
  • uniapp小程序工厂模式示例
    //appBusinessHandle.jsconstdoSomeThing=()=>{...}/*小程序启动时需要处理的个性逻辑*/constonLaunchHandle={}/*小程序显示时需要处理的个性逻辑*/constonS......
  • 代理模式
    动态代理动态的生成代理对象,对满足条件的目标对象皆可以生成代理代理对象,没有静态代理一个目标类一个代理类的问题解耦合 JDK动态代理需要目标类有实现接口代理类与......