编程语言常用设计模式有哪些?

    作者:课课家教育更新于: 2019-08-20 09:35:30

    大神带你学编程,欢迎选课

    Javascript 的一些常用设计模式。java具有简单性、面向对象、分布式、健壮性、安全性、平台独立与可移植性、多线程、动态性等特点 。Java可以编写桌面应用程序、web应用程序、分布式系统和嵌入式系统应用程序等。

    设计模式是前人解决某个特定场景下对而总结出来的一些解决方案。可能刚开始接触编程还没有什么经验的时候,会感觉设计模式没那么好理解,这个也很正常。有些简单的设计模式我们有时候用到,不过没意识到也是存在的。

    编程语言常用设计模式有哪些_编程语言_Java_Javascript_课课家

    学习设计模式,可以让我们在处理问题的时候提供更多更快的解决思路。

    当然设计模式的应用也不是一时半会就会上手,很多情况下我们编写的业务逻辑都没用到设计模式或者本来就不需要特定的设计模式。

    适配器模式

    这个使我们常使用的设计模式,也算最简单的设计模式之一,好处在于可以保持原有接口的数据结构不变动。

    适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。

    例子

    适配器模式很好理解,假设我们和后端定义了一个接口数据结构为(可以理解为旧接口):

    1.   { 
    2.     "label""选择一"
    3.     "value": 0 
    4.   }, 
    5.   { 
    6.     "label""选择二"
    7.     "value": 1 
    8.   } 

    但是后端后面因为其他原因,需要定义返回的结构为(可以理解为新接口):

    1.   { 
    2.     "label""选择一"
    3.     "text": 0 
    4.   }, 
    5.   { 
    6.     "label""选择二"
    7.     "text": 1 
    8.   } 

    然后我们前端的使用到后端接口有好几处,那么我可以把新的接口字段结构适配为老接口的,就不需要各处去修改字段,只要把源头的数据适配好就可以了。

    当然上面的是非常简单的场景,也是经常用到的场景。或许你会认为后端处理不更好了,的确是这样更好,但是这个不是我们讨论的范围。

    单例模式

    单例模式,从字面意思也很好理解,就是实例化多次都只会有一个实例。

    有些场景实例化一次,可以达到缓存效果,可以减少内存占用。还有些场景就是必须只能实例化一次,否则实例化多次会覆盖之前的实例,导致出现 bug(这种场景比较少见)。

    例子

    实现弹框的一种做法是先创建好弹框, 然后使之隐藏, 这样子的话会浪费部分不必要的 DOM 开销, 我们可以在需要弹框的时候再进行创建, 同时结合单例模式实现只有一个实例, 从而节省部分 DOM 开销。下列为登入框部分代码:

    1. const createLoginLayer = function() { 
    2.   const div = document.createElement('div'
    3.   div.innerHTML = '登入浮框' 
    4.   div.style.display = 'none' 
    5.   document.body.appendChild(div) 
    6.   return div 

    使单例模式和创建弹框代码解耦

    1. const getSingle = function(fn) { 
    2.   const result 
    3.   return function() { 
    4.     return result || result = fn.apply(this, arguments) 
    5.   } 
    6. const createSingleLoginLayer = getSingle(createLoginLayer) 
    7.  
    8. document.getElementById('loginBtn').onclick = function() { 
    9.   createSingleLoginLayer() 

    代理模式

    代理模式的定义:为一个对象提供一个代用品或占位符,以便控制对它的访问。

    代理对象拥有本体对象的一切功能的同时,可以拥有而外的功能。而且代理对象和本体对象具有一致的接口,对使用者友好。

    虚拟代理

    下面这段代码运用代理模式来实现图片预加载,可以看到通过代理模式巧妙地将创建图片与预加载逻辑分离,,并且在未来如果不需要预加载,只要改成请求本体代替请求代理对象就行。

    1. const myImage = (function() { 
    2.   const imgNode = document.createElement('img'
    3.   document.body.appendChild(imgNode) 
    4.   return { 
    5.     setSrc: function(src) { 
    6.       imgNode.src = src 
    7.     } 
    8.   } 
    9. })() 
    10.  
    11. const proxyImage = (function() { 
    12.   const img = new Image() 
    13.   img.onload = function() { // http 图片加载完毕后才会执行 
    14.     myImage.setSrc(this.src) 
    15.   } 
    16.   return { 
    17.     setSrc: function(src) { 
    18.       myImage.setSrc('loading.jpg') // 本地 loading 图片 
    19.       img.src = src 
    20.     } 
    21.   } 
    22. })() 
    23.  
    24. proxyImage.setSrc('http://loaded.jpg'

    缓存代理

    在原有的功能上加上结果缓存功能,就属于缓存代理。

    原先有个功能是实现字符串反转(reverseString),那么在不改变 reverseString 的现有逻辑,我们可以使用缓存代理模式实现性能的优化,当然也可以在值改变的时候去处理下其他逻辑,如 Vue computed 的用法。

    1. function reverseString(str) { 
    2.   return str 
    3.     .split(''
    4.     .reverse() 
    5.     .join(''
    6. const reverseStringProxy = (function() { 
    7.   const cached = {} 
    8.   return function(str) { 
    9.     if (cached[str]) { 
    10.       return cached[str] 
    11.     } 
    12.     cached[str] = reverseString(str) 
    13.     return cached[str] 
    14.   } 
    15. })() 

    订阅发布模式

    订阅发布使前端常用的数据通信方式、异步逻辑处理等等,如 React setState 和 Redux 就是订阅发布模式的。

    但是要合理的使用订阅发布模式,否则会造成数据混乱,redux 的单向数据流思想可以避免数据流混乱的问题。

    例子

    1. class Event { 
    2.   constructor() { 
    3.     // 所有 eventType 监听器回调函数(数组) 
    4.     this.listeners = {} 
    5.   } 
    6.   /** 
    7.    * 订阅事件 
    8.    * @param {String} eventType 事件类型 
    9.    * @param {Function} listener 订阅后发布动作触发的回调函数,参数为发布的数据 
    10.    */ 
    11.   on(eventType, listener) { 
    12.     if (!this.listeners[eventType]) { 
    13.       this.listeners[eventType] = [] 
    14.     } 
    15.     this.listeners[eventType].push(listener) 
    16.   } 
    17.   /** 
    18.    * 发布事件 
    19.    * @param {String} eventType 事件类型 
    20.    * @param {Any} data 发布的内容 
    21.    */ 
    22.   emit(eventType, data) { 
    23.     const callbacks = this.listeners[eventType] 
    24.     if (callbacks) { 
    25.       callbacks.forEach((c) => { 
    26.         c(data) 
    27.       }) 
    28.     } 
    29.   } 
    30.  
    31. const event = new Event() 
    32. event.on('open', (data) => { 
    33.   console.log(data) 
    34. }) 
    35. event.emit('open', { opentrue }) 

    观察者模式

    观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个目标对象,当这个目标对象的状态发生变化时,会通知所有观察者对象,使它们能够自动更新。

    Vue 的数据驱动就是使用观察者模式,mbox 也是使用观察者模式。

    例子

    模仿 Vue 数据驱动渲染模式(只是类似,简单的模仿)。

    首先使用 setter 和 getter 监听到数据的变化:

    1. const obj = { 
    2.   data: { description: '' }, 
    3.  
    4. Object.defineProperty(obj, 'description', { 
    5.   get() { 
    6.     return this.data.description 
    7.   }, 
    8.   set(val) { 
    9.     this.data.description = val 
    10.   }, 
    11. }) 

    然后加上目标和观察者

    1. class Subject { 
    2.   constructor() { 
    3.     this.observers = [] 
    4.   } 
    5.  
    6.   add(observer) { 
    7.     this.observers.push(observer) 
    8.   } 
    9.  
    10.   notify(data) { 
    11.     this.observers.forEach((observer) => observer.update(data)) 
    12.   } 
    13.  
    14. class Observer { 
    15.   constructor(callback) { 
    16.     this.callback = callback 
    17.   } 
    18.   update(data) { 
    19.     this.callback && this.callback(data) 
    20.   } 
    21.  
    22. // 创建观察者ob1 
    23. let ob1 = new Observer((text) => { 
    24.   document.querySelector('#dom-one').innerHTML(text) 
    25. }) 
    26. // 创建观察者ob2 
    27. let ob2 = new Observer((text) => { 
    28.   document.querySelector('#dom-two').innerHTML(text) 
    29. }) 
    30. // 创建目标sub 
    31. let sub = new Subject() 
    32. // 目标sub添加观察者ob1 (目标和观察者建立了依赖关系) 
    33. sub.add(ob1) 
    34. // 目标sub添加观察者ob2 
    35. sub.add(ob2) 
    36. // 目标sub触发事件(目标主动通知观察者) 
    37. sub.notify('这里改变了'

    组合在一起是这样的

    1.  
    2.  
    3.    
    4.     "utf-8" /> 
    5.     
    6.       name="viewport" 
    7.       content="width=device-width,initial-scale=1,maximum-scale=1,viewport-fit=cover" 
    8.     /> 
    9.      
    10.    
    11.    
    12.     "app"> 
    13.       "dom-one"> 
    14.         原来的值 
    15.       
     
  •        
  •       "dom-two"> 
  •         原来的值 
  •       
  •  
  •        
  •       "btn">改变 
  •     
 
  •      
  •    
  •  
  • 装饰者模式

    装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。

    ES6/7 的decorator 语法提案,就是装饰者模式。

    例子

    1. class A { 
    2.   getContent() { 
    3.     return '第一行内容' 
    4.   } 
    5.   render() { 
    6.     document.body.innerHTML = this.getContent() 
    7.   } 
    8.  
    9. function decoratorOne(cla) { 
    10.   const prevGetContent = cla.prototype.getContent 
    11.   cla.prototype.getContent = function() { 
    12.     return ` 
    13.       第一行之前的内容 
    14.       
       
    15.       ${prevGetContent()} 
    16.     ` 
    17.   } 
    18.   return cla 
    19.  
    20. function decoratorTwo(cla) { 
    21.   const prevGetContent = cla.prototype.getContent 
    22.   cla.prototype.getContent = function() { 
    23.     return ` 
    24.       ${prevGetContent()} 
    25.       
       
    26.       第二行内容 
    27.     ` 
    28.   } 
    29.   return cla 
    30.  
    31. const B = decoratorOne(A) 
    32. const C = decoratorTwo(B) 
    33. new C().render() 

    策略模式

    在策略模式(Strategy Pattern)中,一个行为或其算法可以在运行时更改。

    假设我们的绩效分为 A、B、C、D 这四个等级,四个等级的奖励是不一样的,一般我们的代码是这样实现:

    1. /** 
    2.  
    3. * 获取年终奖 
    4.  
    5. * @param {String} performanceType 绩效类型, 
    6.  
    7. * @return {Object} 年终奖,包括奖金和奖品 
    8.  
    9. */ 
    10.  
    11. function getYearEndBonus(performanceType) { 
    12.  
    13. const yearEndBonus = { 
    14.  
    15. // 奖金 
    16.  
    17. bonus: ''
    18.  
    19. // 奖品 
    20.  
    21. prize: ''
    22.  
    23.  
    24. switch (performanceType) { 
    25.  
    26. case 'A': { 
    27.  
    28. yearEndBonus = { 
    29.  
    30. bonus: 50000, 
    31.  
    32. prize: 'mac pro'
    33.  
    34.  
    35. break 
    36.  
    37.  
    38. case 'B': { 
    39.  
    40. yearEndBonus = { 
    41.  
    42. bonus: 40000, 
    43.  
    44. prize: 'mac air'
    45.  
    46.  
    47. break 
    48.  
    49.  
    50. case 'C': { 
    51.  
    52. yearEndBonus = { 
    53.  
    54. bonus: 20000, 
    55.  
    56. prize: 'iphone xr'
    57.  
    58.  
    59. break 
    60.  
    61.  
    62. case 'D': { 
    63.  
    64. yearEndBonus = { 
    65.  
    66. bonus: 5000, 
    67.  
    68. prize: 'ipad mini'
    69.  
    70.  
    71. break 
    72.  
    73.  
    74.  
    75. return yearEndBonus 
    76.  

    使用策略模式可以这样:

    1. /** 
    2.  * 获取年终奖 
    3.  * @param {String} strategyFn 绩效策略函数 
    4.  * @return {Object} 年终奖,包括奖金和奖品 
    5.  */ 
    6. function getYearEndBonus(strategyFn) { 
    7.   if (!strategyFn) { 
    8.     return {} 
    9.   } 
    10.   return strategyFn() 
    11.  
    12. const bonusStrategy = { 
    13.   A() { 
    14.     return { 
    15.       bonus: 50000, 
    16.       prize: 'mac pro'
    17.     } 
    18.   }, 
    19.   B() { 
    20.     return { 
    21.       bonus: 40000, 
    22.       prize: 'mac air'
    23.     } 
    24.   }, 
    25.   C() { 
    26.     return { 
    27.       bonus: 20000, 
    28.       prize: 'iphone xr'
    29.     } 
    30.   }, 
    31.   D() { 
    32.     return { 
    33.       bonus: 10000, 
    34.       prize: 'ipad mini'
    35.     } 
    36.   }, 
    37.  
    38. const performanceLevel = 'A' 
    39. getYearEndBonus(bonusStrategy[performanceLevel]) 

    这里每个函数就是一个策略,修改一个其中一个策略,并不会影响其他的策略,都可以单独使用。当然这只是个简单的范例,只为了说明。

    策略模式比较明显的特性就是可以减少 if 语句或者 switch 语句。

    职责链模式

    顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。

    在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

    例子

    1. function order(options) { 
    2.   return { 
    3.     next: (callback) => callback(options), 
    4.   } 
    5.  
    6. function order500(options) { 
    7.   const { orderType, pay } = options 
    8.   if (orderType === 1 && pay === true) { 
    9.     console.log('500 元定金预购, 得到 100 元优惠券'
    10.     return { 
    11.       next: () => {}, 
    12.     } 
    13.   } else { 
    14.     return { 
    15.       next: (callback) => callback(options), 
    16.     } 
    17.   } 
    18.  
    19. function order200(options) { 
    20.   const { orderType, pay } = options 
    21.   if (orderType === 2 && pay === true) { 
    22.     console.log('200 元定金预购, 得到 50 元优惠券'
    23.     return { 
    24.       next: () => {}, 
    25.     } 
    26.   } else { 
    27.     return { 
    28.       next: (callback) => callback(options), 
    29.     } 
    30.   } 
    31.  
    32. function orderCommon(options) { 
    33.   const { orderType, stock } = options 
    34.   if (orderType === 3 && stock > 0) { 
    35.     console.log('普通购买, 无优惠券'
    36.     return {} 
    37.   } else { 
    38.     console.log('库存不够, 无法购买'
    39.   } 
    40.  
    41. order({ 
    42.   orderType: 3, 
    43.   pay: true
    44.   stock: 500, 
    45. }) 
    46.   .next(order500) 
    47.   .next(order200) 
    48.   .next(orderCommon) 
    49. // 打印出 “普通购买, 无优惠券” 

    上面的代码,对 order 相关的进行了解耦,order500,order200、orderCommon 等都是可以单独调用的。

     Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程  。
    标签: 编程语言JavaJavascript

    上一篇:编程语言最常用的开发工具

    下一篇:人工智能可以帮助我们应对哪些社会挑战?

    为您推荐

    课课家教育

    未登录