Face Oriented Programming and Decoration

Keywords: Javascript Programming Spring Database Attribute

AoP

~~~~~mm, Baidu Encyclopedia once~~~

AOP is the abbreviation of Aspect Oriented Programming, which means: facet-oriented programming, a technology for unified maintenance of program functions through precompilation and run-time dynamic agents.AOP is the continuation of OOP, a hot spot in software development, an important content in Spring framework, and a derived paradigm of functional programming.With AOP, parts of business logic can be isolated, which reduces the coupling between parts of business logic, improves the reusability of programs, and improves the efficiency of development.

  • As a front-end, Xiao Meng understood that it was to separate other businesses from the main process and then cut in from the side.No intrusive modifications to the original function.

Say nothing but a classic online example:

Business logic:

Main Business: Modify Database

Start point 1: Print logs before modification

Start Point 2: Print Log After Modification

Function.prototype.before = function(beforeFunc) {
  let that = this
  return function() {
    beforeFunc.apply(this, arguments)
    return that.apply(this, arguments)
  }
}

Function.prototype.after = function(afterFunc) {
  let that = this
  return function() {
    let ret = that.apply(this, arguments)
    afterFunc.apply(this, arguments)
    return ret
  }
}

function updateDb() {
  console.log(`update db`)
}
function beforeUpdateDb() {
  console.log(`before update db`)
}
function afterUpdateDb() {
  console.log(`updated db`)
}
updateDb = updateDb.before(beforeUpdateDb).after(afterUpdateDb)
updateDb()

Principle: Actually, it wraps the main business updateDb twice and returns a new method.The new method calls the cut-in method before and after the original method call, avoiding direct changes on the main method.

  • Is this poor readability and code maintainability? Here's how you can program facets with an ornament.

Decorator decorator

  • Decorators are incompatible with ES7 modern tourists and require babel translation, plugin-transform-decorators-legacy
  • Decorators can only work on the class itself, its methods or properties, and its access operators
  • Modifier'@'is an identifier

1 Pair of Class Decorations

@create
class Apes {
 
}

// Modify the class itself
function create(className) {
  className.prototype.create = function() {
    console.log('Manufacturing Tools')
  }
  return descriptor
}

let apes1 = new Apes()
apes1.create() 
// Manufacturing Tools
  • Modify the class itself: create(className).Decorators are essentially compile-time functions.
  • To modify subclasses, modify the subclasses by using className.prototype.

2 Method Modifications on Classes

class Apes {
  @eatMore
  eat() {
    console.log('eat fruit')
  }
}
// Modification Method
function eatMore(className, propName, descriptor) {
  //console.log(descriptor)
  let value = descriptor.value
  descriptor.value = function() {
    console.log('Dirt-poor')
    value()
  }
  return descriptor
}

let apes1 = new Apes()
apes1.eat()

  • Decorate eatMore(className, propName, descriptor) on a class's methods
className - Modified class
 propName - Modified property name
 descriptor - Description object for this property

Object.defineProperty dependent on ES5 as seen from the descriptor attribute descriptor

Review Object.defineProperty

Value: The value of the property, defaulting to undefined

writable: Whether the value of the property can be modified, default value is true

enumerable: Can properties be returned through a for-in loop?Default to true

configurable: Whether attributes can be redefined by deleting them, whether attributes can be modified, whether attributes can be modified to accessor attributes, default to true.
  • The above method modifications to classes actually take their methods through descriptor.value and wrap them back
  • You can also directly modify other properties on the descriptor or return a new descriptor

3. Decoration for access operators

class test {
  //@nonenumerable
  get kidCount() {
    return 111
  }
}

function disWirte(target, name, descriptor) {
  descriptor.writable = false
  return descriptor
}
let p = new test()
console.log(p.kidCount)
p.kidCount = 222n descriptor;

// throw
// TypeError: Cannot set property kidCount of #<Person> which has only a getter

4. Modifying Passwords

class Apes {
  @say('Can say Chinese')
  say() {
    console.log('Grilla Bonobo Grilla')
  }
}

// Modify method and pass parameters
function say(str) {
  return function(className, propName, descriptor) {
    descriptor.value = function() {
      console.log(str)
    }
    return descriptor
  }
}
  • Passing parameters by Curitization

application

1. Applying to the calculation of Fibonacci series

const memory = () => {
  const cache = {} //Cache pool
  return (target, name, descriptor) => {
    // Original method
    const method = descriptor.value
    // Packaging method
    descriptor.value = function(key) {
      if (cache[key]) {
        return cache[key]
      }
      const ret = method.apply(target, [key])
      cache[key] = ret
      return ret
    }
    return descriptor
  }
}

let count = 0

class Test {
  @memory()
  fb(n) {
    count++
    if (n === 1) return 1
    if (n === 2) return 1
    return this.fb(n - 1) + this.fb(n - 2)
  }
}

const t = new Test()
console.log(t.fb(10))
console.log(count)

2. Type checking on access operator-set

class test {
  constructor() {
    this.a = 1
  }
  get a() {
    return this.a
  }
  @check('number')
  set a(v) {
    return v
  }
}

function check(type) {
  return function(target, prop, descriptor) {
    let v = descriptor.value
    return {
      enumerable: true,
      configurable: true,
      get: function() {
        return v
      },
      set: function(c) {
        var curType = typeCheck(c)
        if (curType !== type) {
          throw `${prop}Must be ${type}type`
        }
        v = c
      }
    }
  }
}

function typeCheck(c) {
  if (c === undefined) {
    return undefined
  }
  if (c === null) {
    return null
  }
  let type = typeof c
  if (type === 'object') {
    return c.constructor == Array ? 'array' : 'object'
  }
  return type
}

let t = new test(11)
t.a = 2
console.log(t.a) // 2

t.a = []
console.log(t.a)
// throw a must be of type number

Posted by bbristow on Thu, 08 Aug 2019 18:06:51 -0700