问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

什么是设计模式,该如何使用设计模式

发布网友 发布时间:2022-04-23 18:35

我来回答

3个回答

懂视网 时间:2022-05-15 05:43

上边这句话,从字面来看很简单。但是如何在开发过程中去应用,仅凭一个定义依然是一头雾水。以笔者曾经做过的商户进销存系统为例:

某超市准备举行促销活动,市场人员经过调查分析制定了一些促销策略:
  1. 购物满100减10

  2. 购物满200减30

  3. 购物满300减50

  4. 。。。

收银软件的界面是这样的(简单示意):

图片描述

我们应该如何计算实际消费金额?

最初的实现是这样的:

//方便起见,我们把各个促销策略定义为枚举值:0,1,2...
var getActualTotal = function(onSaleType,originTotal){
 if(onSaleType===0){
 return originTotal-Math.floor(originTotal/100)*10
 }
 if(onSaleType===1){
 return originTotal-Math.floor(originTotal/200)*30
 }
 if(onSaleType===0){
 return originTotal-Math.floor(originTotal/300)*50
 }
}
getActualTotal(1,2680); //2208

上面这段代码很简单,而且缺点也很明显。随着我的满减策略逐渐增多,getActualTotal函数会越变越大,而且充满了if判断,稍一疏忽就容易弄错。

OK,有人说我很懒,虽然这样不够优雅但并不影响我的使用,毕竟满减策略再多也多不到哪去。
我只能说,需求永远不是程序员定的。。这时,市场人员说我们新版程序添加了会员功能,我们需要支持以下的促销策略:

会员促销策略:
  1. 会员充300返60,且首单打9折

  2. 会员充500返100,且首单打8折

  3. 会员充1000返300,且首单打7折

  4. ...

这个时候,如果你还在原先的getActualTotal函数中继续添加if判断,我想如果你的领导review你这段代码,可能会怀疑自己当初怎么把你招进来。。

OK,我们终于下定决心要重构促销策略的代码,我们可以这么做:

var vipPolicy_0=function(originTotal){
 return originTotal-Math.floor(originTotal/100)*10
}
var vipPolicy_1=function(originTotal){
 return originTotal-Math.floor(originTotal/200)*30
}
...
//会员充1000返300
var vipPolicy_10=function(account,originTotal){
 if(account===0){
 account+=1300;
 return originTotal*0.9
 }else{
 account+=1300;
 return originTotal;
 }
 return originTotal-Math.floor(originTotal/200)*30
}
...
var vipPolicy_n=function(){
 ...
}
var getActualTotal=function(onSaleType,originTotal,account){
 switch(onSaleType){
 case 0:
  return vipPolicy_0(originTotal);
 case 1:
  return vipPolicy_0(originTotal);
 ...
 case n:
  return ...
 default:
  return originTotal;
 }
}

好了,现在我们每种策略都有自己独立的空间了,看起来井井有条。但是还有两个问题没有解决:

  1. 随着促销策略的增加,getActualTotal的代码量依然会越来越大

  2. 系统缺乏弹性,如果需要增加一种策略,那么除了添加一个策略函数,还需要修改switch...case..语句

让我们再来回顾一下策略模式的定义:

定义一系列的算法,把它们一个个封装起来,并且使它们可互相替换

在我们的例子中,每种促销策略的实现方式是不一样的,但我们最终的目的都是为了求得实际金额。策略模式可以把我们对促销策略的算法一个个封装起来,并且使它们可互相替换而不影响我们对实际金额的求值,这正好是我们所需要的。

下面我们用策略模式来重构上面的代码:

var policies={
 "Type_0":function(originTotal){
 return originTotal-Math.floor(originTotal/100)*10 
 },
 "Type_1":function(originTotal){
 return originTotal-Math.floor(originTotal/200)*30 
 },
 ...
 "Type_n":function(originTotal){
 ... 
 }
}
var getActualTotal=function(onSaleType,originTotal,account){
 return policies["Type_"+onSaleType](originTotal,account)
}
//执行
getActualTotal(0,2680.00);//2208

分析上面的代码我们发现,不管促销策略如何增加,getActualTotal函数完全不需要再变化了。我们要做的,就是增加新策略的函数而已。

通过策略模式的代码,我们消除了让人反胃的大片条件分支语句,getActualTotal本身并没有计算能力,而是将计算全权委托给了策略函数。

由此我们可以总结出策略模式实现的要点:

  1. 将变化的算法封装成独立的策略函数,并负责具体的计算

  2. 委托函数,该函数接受客户请求,并将请求委托给某一个具体的策略函数

用一张UML图表示如下:
图片描述

怎么样?现在看到上面这张图是不是有了了然于胸的感觉?那就赶紧去试一试策略模式吧!


参考书籍:

  1. 《设计模式:可复用面向对象软件的基础》

  2. 《大话设计模式》

  3. 《Javascript设计模式与开发实践》

做前端开发已经好几年了,对设计模式一直没有深入学习总结过。随着架构相关的工作越来越多,越来越能感觉到设计模式成为了我前进道路上的一个阻碍。所以从今天开始深入学习和总结经典的设计模式以及面向对象的几大原则。

今天第一天,首先来讲策略模式。

什么是策略模式?

GoF四兄弟的经典《设计模式》中,对策略模式的定义如下:

定义一系列的算法,把它们一个个封装起来,并且使它们可互相替换。

上边这句话,从字面来看很简单。但是如何在开发过程中去应用,仅凭一个定义依然是一头雾水。以笔者曾经做过的商户进销存系统为例:

某超市准备举行促销活动,市场人员经过调查分析制定了一些促销策略:
  1. 购物满100减10

  2. 购物满200减30

  3. 购物满300减50

  4. 。。。

收银软件的界面是这样的(简单示意):

图片描述

我们应该如何计算实际消费金额?

最初的实现是这样的:

//方便起见,我们把各个促销策略定义为枚举值:0,1,2...
var getActualTotal = function(onSaleType,originTotal){
 if(onSaleType===0){
 return originTotal-Math.floor(originTotal/100)*10
 }
 if(onSaleType===1){
 return originTotal-Math.floor(originTotal/200)*30
 }
 if(onSaleType===0){
 return originTotal-Math.floor(originTotal/300)*50
 }
}
getActualTotal(1,2680); //2208

上面这段代码很简单,而且缺点也很明显。随着我的满减策略逐渐增多,getActualTotal函数会越变越大,而且充满了if判断,稍一疏忽就容易弄错。

OK,有人说我很懒,虽然这样不够优雅但并不影响我的使用,毕竟满减策略再多也多不到哪去。
我只能说,需求永远不是程序员定的。。这时,市场人员说我们新版程序添加了会员功能,我们需要支持以下的促销策略:

会员促销策略:
  1. 会员充300返60,且首单打9折

  2. 会员充500返100,且首单打8折

  3. 会员充1000返300,且首单打7折

  4. ...

这个时候,如果你还在原先的getActualTotal函数中继续添加if判断,我想如果你的领导review你这段代码,可能会怀疑自己当初怎么把你招进来。。

OK,我们终于下定决心要重构促销策略的代码,我们可以这么做:

var vipPolicy_0=function(originTotal){
 return originTotal-Math.floor(originTotal/100)*10
}
var vipPolicy_1=function(originTotal){
 return originTotal-Math.floor(originTotal/200)*30
}
...
//会员充1000返300
var vipPolicy_10=function(account,originTotal){
 if(account===0){
 account+=1300;
 return originTotal*0.9
 }else{
 account+=1300;
 return originTotal;
 }
 return originTotal-Math.floor(originTotal/200)*30
}
...
var vipPolicy_n=function(){
 ...
}
var getActualTotal=function(onSaleType,originTotal,account){
 switch(onSaleType){
 case 0:
  return vipPolicy_0(originTotal);
 case 1:
  return vipPolicy_0(originTotal);
 ...
 case n:
  return ...
 default:
  return originTotal;
 }
}

好了,现在我们每种策略都有自己独立的空间了,看起来井井有条。但是还有两个问题没有解决:

  1. 随着促销策略的增加,getActualTotal的代码量依然会越来越大

  2. 系统缺乏弹性,如果需要增加一种策略,那么除了添加一个策略函数,还需要修改switch...case..语句

让我们再来回顾一下策略模式的定义:

定义一系列的算法,把它们一个个封装起来,并且使它们可互相替换

在我们的例子中,每种促销策略的实现方式是不一样的,但我们最终的目的都是为了求得实际金额。策略模式可以把我们对促销策略的算法一个个封装起来,并且使它们可互相替换而不影响我们对实际金额的求值,这正好是我们所需要的。

下面我们用策略模式来重构上面的代码:

var policies={
 "Type_0":function(originTotal){
 return originTotal-Math.floor(originTotal/100)*10 
 },
 "Type_1":function(originTotal){
 return originTotal-Math.floor(originTotal/200)*30 
 },
 ...
 "Type_n":function(originTotal){
 ... 
 }
}
var getActualTotal=function(onSaleType,originTotal,account){
 return policies["Type_"+onSaleType](originTotal,account)
}
//执行
getActualTotal(0,2680.00);//2208

分析上面的代码我们发现,不管促销策略如何增加,getActualTotal函数完全不需要再变化了。我们要做的,就是增加新策略的函数而已。

通过策略模式的代码,我们消除了让人反胃的大片条件分支语句,getActualTotal本身并没有计算能力,而是将计算全权委托给了策略函数。

由此我们可以总结出策略模式实现的要点:

  1. 将变化的算法封装成独立的策略函数,并负责具体的计算

  2. 委托函数,该函数接受客户请求,并将请求委托给某一个具体的策略函数

用一张UML图表示如下:
图片描述

怎么样?现在看到上面这张图是不是有了了然于胸的感觉?那就赶紧去试一试策略模式吧!

相信看了本文案例你已经掌握了方法,更多精彩请关注Gxl网其它相关文章!

推荐阅读:

怎样使用JS+H5实现微信摇一摇

如何对微信小程序进行开发

热心网友 时间:2022-05-15 02:51

设计模式是面向对象编程的热门话题之一,越来越多的开发人员认识到设计模式的重要性。采用各种语言实现设计模式的文章也越来越多,但是很多开发人员发现很难将设计模式与实际开发中需要解决的具体问题相联系。因为使用设计模式的难点往往不在于模式的实现,而在于很难确定哪种模式可以在现实的应用场景中采用,从而导致了在现实的项目中,面对客户的压力,我们总是采用最直截了当的方法解决问题,来不及多考虑这些方法的优劣,即使明知将带来更大的麻烦也必须如此。有些时候因为选择了不恰当的设计模式,使原本简单的问题变得复杂化。
总是有些优秀的设计人员可以在同样短的时间内做出正确对待的判断,他们同样是依靠本能和直觉,只是这种本能是在日常编程开发中一点一滴积累起来的。如同一个剑客在危机时刻的一击,并不是一时的灵光乍现,而是平时刻苦*的结果。
俗话说,紧靠背棋谱成不了围棋高手。只在概念上理解设计模式而不实现,同样成不了架构设计师。在软件设计时,要有意识地问自己使用还是不使用设计模式,不要匆忙下结论。重视软件质量的改进,如果有可能,则在项目后期重构代码。同时注意学习同行的经验,很多开放源码项目是值得学习的。
(1)正确理解设计模式
模式所关注的不仅是重复的解决方案,更主要的是关注重复出现的应用场景和与场景相关的各种作用力。很多使用设计模式失败的原因,并不是实现设计模式的方法有问题,而是采用的设计模式不适合应用场景。这往往导致设计过度,使软件应得复杂,进而丧失对使用设计模式的信心。
(2)编程语言与设计模式的实现
尽管设计模式本身并不要求一定用某种语言来实现,但脱离了具体的实现,就无法真正理解设计模式。GOF的《设计模式》是经典之作,但毕竟距现在已经十几年了。这个期间开发平台已经进化了多代,很多新技术已经应用到编程中。有些技术可以简化设计模式的实现,有些技术已经采用了设计模式。因此,学习设计模式必须针对所使用的编程语言和开发平台。一定要注意,不是将《设计模式》中的例子转换为C#或者其他语言就等于知道如何实现设计模式了,而是要关注设计模式的精髓,并结合具体的语言特点完成其实现。就.NET而言,很多技术可以简化设计模式的实现,例如采用反射技术实现工厂和采用委托技术实现模板方法等。
(3)需求驱动
需求驱动不仅仅是功能性需求,还包括性能需求及运行时的需求,如软件的可维护性和可复用性等方面。
设计模式是针对软件设计的,而软件设计是针对需求的,一定不要为了使用模式而使用模式。在不合适的场合生搬硬套地使用模式反而会使设计应得复杂,使软件难以调试和维护。
(4)分析成功的模式应用项目
置之死地而后生可以说是一种解决方案,而不是模式,或者说仅仅给出了模式的实现,而没有交代使用的场合。项羽采用这个方案把秦军打败了,但马谡却丢了街亭。
(5)充分了解所使用的开发平台。
总的来说,设计模式是针对面向对象的软件设计的,因此在理论上适合任何面向对象的语言。但随着技术的发展和编程环境的改善,设计模式的实现方式会有很大的差别。在某些平台下,某些设计模式是自然实现的,某些模式已经被平台所实现,某些模式存在的上下文已经消失。
这里的平台不仅指编程语言,还包括平台引入的技术。.NET平台引进了反射、委托,以及属性等新技术,这些技术的使用使设计模式的实现方式有了很大的改变。例如,工厂方法通过采用反射技术,可以将其中的子类去掉。这实际上已经是一个.NET下的新模式,或者说是.NET的方言。
(6)在编程中领悟模式
软件开发是一项实践工作,最直接的方法就是编程。没有定式很熟却从来不下棋的围棋高手,也没有不会编程就成为架构设计师的先例。对设计模式的掌握是水到渠成的事情,你可能是顿悟,也可能是渐悟,但前提是必须有相当的实践积累。当然,并不是不需要看书学习,但实践仍然是必须首先要重视的。
认为编程如同写文章,提高需要有一个过程。在多多编程的同时,需要有一定的技巧。如果希望水平有较大提高,则需要对自己编写的代码不断重构。力求最优是个很好的习惯,当然前提是项目进度允许。即使项目时间紧张,也需要进行适当的总结。隔一段时间检查一下以前的工作,会发现自己是否已经有了提高。
(7)避免设计过度
设计模式解决的是设计不足的问题,但同时也要避免设计过度。一定要牢记简洁原则(Keep It Simple, Stupid, KISS),要知道,设计模式是为了使设计简单,而不是更复杂。如果引入设计模式使设计变得复杂,只能说我们把简单的问题复杂化了,问题本身不需要设计模式。
这里需要把握的是需求变化的程度,一定要区分需求的稳定篇和可变篇。一个软件必然有稳定的篇,这个篇就是核心业务逻辑。如果核心业务逻辑发生变化,软件就没有存在的必要,这个篇的逻辑是我们需要固化的。对于可变的篇,需要判断可能发生变化的程度来确定设计策略和设计风险。要知道,设计过度与设计不足同样对项目有害。
(8)合理看待设计模式的实现实例
现在,从各种途径可以发现各种设计模式的实现实例。需要说明的是,其中很多实例所说明的仅仅是设计模式的解决方案的实现,并没有分析模式使用的上下文。实际上,这也是最困难的篇——从而导致实例中的设计模式使用从实践的角度看,往往是过度设计,也就是有小题大做的嫌疑。
对模式感兴趣的朋友可以从下面的几个开源项目中学习模式的成功应用。以后可能会把模式在下面几个开源代码中的应用的文章与大家共享。

热心网友 时间:2022-05-15 04:09

看看介绍和讲解吧:网页链接,是一种思想,一种场景下的解决方案,要理解了,才能用。

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
为什么来大姨妈胸会胀 少儿学什么舞蹈 青年学什么舞蹈好 成年人学什么舞蹈 福州企业最低工资标准 2013年厦门的底薪是多少 生产要素的需求有哪些性质 生产要素的需求有何特点? 什么是生产要素需求 微观经济学要素需求什么是条件要素需求?它和要素需求有什么不同?_百度... CPU中有一个程序计数器(又称指令计数器),它用于存放 如何正确学习和使用设计模式 程序计数器PC和寄存器EIP有什么关系吗,为什么它们都是存放下一条指令... 通常在软件开发中设计模式都有哪些原则呢? 怎么学好设计模式 程序计数器(PC)作为不可寻址寄存器,它有哪些特点? 单片机:PC寄存器中的内容的含义是什么? 程序计数器pc用于做什么? 程序计数器PC中存放的指令地址,这个地址指字地址吗?然后需要译码为具体的内存字节地址? cpu中指令计数器的作用是啥啊? pc是什么寄存器作用是什么 程序计数器是什么东西? 程序计数器有什么作用? CPU中有一个程序计数器(又称指令计数器),它主要用于存放什么? pc用来存放什么 51单片机的程序计数器PC的作用 北京爱彼手表如何保养? 长虹电视是哪个国家的产品? 天津爱彼手表要多长时间保养一次 有大神知道,爱彼的手表大小保养分别有哪些内容么 在8051单片机中程序计数器PC位于内部RAM中的那一个单元地址 初学软件设计模式应该读哪本书? 如何理解软件设计模式? 敏捷软件开发之3种生活场景巧妙解读设计模式 怎么做需求分析,学习软件设计模式? 软件设计模式的简介 海尔冰箱BCD-356WACB的制冰机不下水是怎么回事? 海尔卡萨帝六门冰箱制冰机制冰不能…… 请问我买的海尔冰箱为什么水不往制冰盒流 海尔卡萨帝冰箱*是水到*器里还是直接出来的就是冰块 我家冰箱水不向下流 在火车站自动售票机买的票12316能查到记录? 12316火车票查询预定周凤珍 12316火车票查询松原到北京 12316火车票查询松原到乌兰浩特都有几点的车 怎么加入呀? 12316火车票查询本手机所买的火票我查询本手机所订的火车票明细表 海尔冰箱漏水咋办 怎么在12316上买动车票? 电冰箱不制作冰块的原因 海尔冰箱,冰箱其他功能正常,*盒内一直有水,就是不结冰块。 12316火车票查询怎么把已添加的人删掉!