1 <!DOCTYPE html>
2 <html>
3 <head>
4 <title></title>
5 <meta charset="UTF-8"/>
6 </head>
7 <body>
8 <img src="1.jpg" alt=""/>
9
10 <script>
11 /*
12 设计原则
13
14 1.找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
15
16 把会变化的部分取出并“封装”起来,好让其他部分不会受到影响。代码变化引起的不经意后果变少,系统变得更有弹性。
17
18 几乎是每个设计模式的精神所在。所有的模式都提供了一套方法让“系统中的某部分改变不会影响其他部分”。
19
20
21 2.针对接口编程,而不是针对实现编程。
22
23 针对接口编程真正的意思是“针对超类型(supertype)编程”。
24 变量的声明类型应该是超类型,通常是一个抽象类或者是一个接口,如此,只要是具体实现此超类型的类所产生的对象,都可以指定给这个变量。这也意味着,声明类时不用理会以后执行时的真正对象类型
25
26
27 3.多用组合,少用继承。
28 。
29 */
30
31 /**
32 * 策略模式
33 *
34 * 定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
35 *
36 * 本质:
37 * 分离算法,选择实现。
38 *
39 * 策略模式的重心不是如何实现算法,而是如何组织,调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性。
40 *
41 * 这里鸭子的行为即是算法族。
42 *
43 * 为了实现让算法能独立于使用它的客户,策略模式引入了一个上下文对象,这个对象负责持有算法,但是不负责绝对具体选用哪个算法,把选择算法的功能交给了客户,由客户选择好具体的算法后,设置到上下文对象中,让上下文对象持有客户选择的算法。当客户通知上下文对象执行功能的时候,上下文对象则转调具体的算法。这样一来,具体的算法和直接使用算法的客户是分离的。
44 * 具体的算法和使用它的客户分离以后,使得算法可独立于使用它的客户而变化,并且能够动态地切换需要使用的算法,只要客户端动态地选择使用不同的算法,然后设置到上下文对象中,在实际调用的时候,就可以调用到不同的算法。
45 *
46 * 1.功能
47 * 策略模式的功能是把具体的算法实现从具体的业务处理中独立出来,把它们实现成为单独的算法类,从而形成一系列的算法,并让这些算法可以相互替换。
48 * 策略模式的中心不是如何来实现算法,而是如何组织,调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性。
49 *
50 * 2.策略模式和if-else语句
51 * 多个if-else语句表达的就是一个平等的功能结构。而策略模式就是把各个平等的具体实现封装到单独的策略实现类了,然后通过上下文来与具体的策略类进行交互。
52 * 因此多个if-else语句可以考虑使用策略模式。
53 *
54 * 3.算法的平等性
55 * 策略模式的很大的特点就是各个策略算法的平等性。所有策略算法在实现上也是相互独立的,相互之间没有依赖的。
56 * 所以策略算法是相同行为的不同实现。
57 *
58 * 4.谁来选择具体的策略算法
59 * 一个是在客户端,当使用上下文的时候,由客户端来选择具体的策略算法,然后把这个策略算法设置给上下文。
60 * 另一个是由上下文来选择具体的策略算法。
61 *
62 * 5.运行时策略的唯一性
63 * 运行期间,策略模式在每一个时刻只能使用一个具体的策略实现对象,虽然可以动态地在不同的策略实现中切换,但使用时只能使用一个。
64 *
65 * 6.增加新的策略
66 * 策略模式可以让你很灵活地扩展新的算法。
67 *
68 *
69 * 策略模式的调用顺序
70 * 1.先是客户端来选择并创建具体的策略对象。
71 * 2.然后客户端创建上下文。
72 * 3.接下来客户端就可以调用上下文的方法来执行功能了,在调用的时候,从客户端传入算法需要的参数。
73 * 4.上下文接到客户的调用请求,会把这个转发给它持有的Strategy。
74 *
75 *
76 * Context和Strategy的关系
77 * 通常是上下文使用具体的策略实现对象,反过来,策略实现对象也可以从山下问获取所需要的数据。
78 *
79 *
80 * 策略模式结合模板方法模式
81 * 对于一系列算法的实现上存在公共功能的情况,策略模式可以有以下三种实现方式:
82 * 1.在上下文当中实现公共功能,让所有具体的策略算法回调这些方法。
83 * 2.将策略的借口改成抽象类,然后在其中实现具体算法的公共功能。
84 * 3.为所有的策略算法定义一个抽象的父类,让这个父类去实现策略的接口,然后在这个父类中去实现公共的功能。
85 * 如果这个时候发现一系列算法的实现步骤都是一样的,只是在某些局部步骤上有所不用的情况,那就可以在这个抽象类里面定义算法实现的骨架,然后让具体的策略算法去实现变化的部分。这样的一个结构自然就变成策略模式结合模板方法模式了。那个抽象类就成了模板方法模式的模板类。
86 */
87
88 (function () {
89 // 示例代码
90
91 // 具体算法
92 function ConcreteStrategyA() {}
93
94 ConcreteStrategyA.prototype.algorithmInterface = function () {/*具体算法的实现*/};
95
96 function ConcreteStrategyB() {}
97
98 ConcreteStrategyB.prototype.algorithmInterface = function () {};
99
100 function ConcreteStrategyC() {}
101
102 ConcreteStrategyC.prototype.algorithmInterface = function () {};
103
104 // 上下文对象,通常会持有一个具体的策略对象
105 function Context(strategy) {
106 this.strategy = strategy;
107 }
108
109 //上下文对客户端提供的操作接口,可以有参数和返回值
110 Context.prototype.contextInterface = function () {
111 // 转调具体的策略对象进行算法运算
112 this.strategy.algorithmInterface();
113 };
114
115 }());
116
117 (function () {
118 // 容错恢复机制
119 function DbLog() {
120 this.log = function (msg) {
121 if (msg && msg.trim().length > 5) {
122 fds; // make mistake
123 }
124 console.log('现在把' + msg + '记录到数据库中');
125 };
126 }
127
128 function FileLog() {
129 this.log = function (msg) {
130 console.log('现在把' + msg + '记录到文件中');
131 };
132 }
133
134 function LogContext() {
135 this.log = function (msg) {
136 var strategy = new DbLog();
137 try {
138 strategy.log(msg);
139 } catch (e) {
140 strategy = new FileLog();
141 strategy.log(msg);
142 }
143 };
144 }
145
146 var log = new LogContext();
147 log.log('"记录日志"');
148 log.log('"再次记录日志"');
149
150 }());
151
152
153 /*
154 适用性
155
156 1.许多相关的类仅仅是行为有异。“策略”提供了一种同多个行为中的一个行为来配置一个类的方法。
157 2.
158 需要使用一个算法的不同变体。例如,你可能会定义一些反映不同的空间/时间权衡的算法。当这些变体实现为一个算法的类层次时,可以使用策略模式。
159 3.算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的,与算法相关的数据结构。
160 4.一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。
161 */
162
163 /*
164 优点:
165
166 1.定义了一系列算法。
167 2.避免多重条件语句。
168 3.更好的扩展性。
169
170 缺点:
171
172 1.客户必须了解每种策略的不同。
173 2.增加了对象数目。
174 3.只适合扁平的算法结构。
175 对于出现需要嵌套使用多个算法的情况,可以考虑使用装饰者模式,或是变形的职责链模式。
176
177
178 相关模式
179 策略模式和状态模式
180 从模式结构上看是一样的,但是实现的功能确实不一样的。
181 状态模式是根据状态的变化来选择相应的行为,不同的状态对应不同的类,每个状态对应的类实现了该状态对应的功能,在实现功能的同时,还会维护状态数据的变化。这些实现状态对应的功能的类之间是不能相互替换的。策略模式是根据需要或者是客户端的要求来选择相应的实现类,各个实现类是平等的,是可以相互替换的。
182 另外策略模式可以让客户端来选择需要使用的策略算法;而状态模式一般是上下文,或者是在状态实现类里面来维护具体的状态数据们通常不由客户端来制定状态。
183
184 策略模式和模板方法模式
185 可组合使用。模板方法重在封装算法骨架,而策略模式重在分离并封装算法实现。
186
187 策略模式和享元模式
188 可组合使用。
189
190 */
191
192
193 (function () {
194 // 抽象上下文类
195 var Duck = function (quackBehavior) {
196 this.quackBehavior = quackBehavior;
197 };
198 Duck.prototype = {
199 display: function () {
200 throw new Error('This is abstract method');
201 },
202 performQuack: function () {
203 this.quackBehavior.quack();
204 },
205 swim: function () {
206 console.log('All ducks float, even decoys');
207 }
208 };
209
210 // 具体上下文类
211 var MallardDuck = function () {
212 Duck.apply(this, arguments);
213 };
214 MallardDuck.prototype.__proto__ = Duck.prototype;
215 MallardDuck.prototype.display = function () {
216 console.log('I\'m a real Mallard duck.');
217 };
218
219 // 抽象算法类
220 var QuackBehavior = function () {
221 };
222 QuackBehavior.prototype.quack = function () {
223 throw new Error('this is an interface');
224 };
225
226 // 具体算法类
227 var MuteQuack = function () {
228 };
229 MuteQuack.prototype.__proto__ = QuackBehavior.prototype;
230 MuteQuack.prototype.quack = function () {
231 console.log('slience');
232 };
233
234 // 客户端
235 var MiniDucksSimulator = function () {
236 this.main();
237 };
238 MiniDucksSimulator.prototype = {
239 main: function () {
240 var mallard = new MallardDuck(new MuteQuack());
241 mallard.performQuack();
242 }
243 };
244 new MiniDucksSimulator();
245
246 // 动态设定行为
247 Duck.prototype.setQuackBehavior = function (qb) {
248 this.quackBehavior = qb;
249 };
250
251 var ModelDuck = function () {
252 Duck.apply(this, arguments);
253 };
254 ModelDuck.prototype.__proto__ = Duck.prototype;
255 ModelDuck.prototype.display = function () {
256 console.log('T\'m a model duck.');
257 };
258
259 var QuackSpeakerPowered = function () {
260 };
261 QuackSpeakerPowered.prototype.quack = function () {
262 console.log('I\'m quacking with a speaker');
263 };
264
265 MiniDucksSimulator.prototype.main = function () {
266 var model = new ModelDuck(new MuteQuack());
267 model.performQuack();
268 model.setQuackBehavior(new QuackSpeakerPowered());
269 model.performQuack();
270 };
271 new MiniDucksSimulator();
272
273
274 // 策略,定义计算报价算法的接口
275 var Strategy = function () {
276 };
277 Strategy.prototype.calcPrice = function (goodsPrice) {
278 throw new Error('This is an abstract interface');
279 };
280
281 var NormalCustomerStrategy = function () {
282 };
283 NormalCustomerStrategy.prototype.__proto__ = Strategy.prototype;
284 NormalCustomerStrategy.prototype.calcPrice = function (goodsPrice) {
285 console.log('对于新客户或者普通客户,没有折扣');
286 return goodsPrice;
287 };
288
289 // 具体算法实现
290 var OldCustomerStrategy = function () {
291 };
292 OldCustomerStrategy.prototype.__proto__ = Strategy.prototype;
293 OldCustomerStrategy.prototype.calcPrice = function (goodsPrice) {
294 console.log('对于老客户,统一折扣5%');
295 return goodsPrice * (1 - 0.05);
296 };
297
298 // 价格管理,主要完成计算向客户所报价格的功能
299 var Price = function (strategy) {
300 this.strategy = strategy;
301 };
302 Price.prototype.quote = function (goodsPrice) {
303 return this.strategy.calcPrice(goodsPrice);
304 };
305
306 // 选择并创建需要使用的策略对象
307 var strategy = new OldCustomerStrategy();
308 // 创建上下文
309 var ctx = new Price(strategy);
310 // 计算报价
311 var quote = ctx.quote(1000);
312 console.log('向客户报价:' + quote);
313 }());
314
315
316 (function () {
317 // 表单验证
318 var r_space = /\s+/;
319
320 // HTML转义
321 var ENCODECHAR = {
322 '<': '<',
323 '>': '>',
324 '&': '&',
325 '"': '"'
326 };
327
328 // 验证策略
329 var VALIDTYPES = {
330 'nonEmpty': {
331 validate: function (value) {
332 return value !== '';
333 },
334 msg: '此项不能为空'
335 },
336 'email': {
337 validate: function (value) {
338 return (/^[\w\-]+@[\w\-]+(?:\.[\w\-]+)+$/.test(value));
339 },
340 msg: function (value) {
341 return (value ? '请输入正确格式的邮箱' : '请输入你的邮箱');
342 }
343 },
344 'phone': {
345 validate: function (value) {
346 return (/^1[3458]\d{9}$/.test(value));
347 },
348 msg: function (value) {
349 return (value ? '请输入正确格式的手机号码' : '请输入你的手机号码');
350 }
351 }
352 };
353
354 var formHooks = {
355 'radio': 'checked',
356 'checkbox': 'checked'
357 };
358
359 var formEventsHooks = {
360 'text': formEventsGetter('blur'),
361 'textarea': formEventsGetter('blur'),
362 'checkbox': formEventsGetter('click'),
363 'select': formEventsGetter('change'),
364 'radio': formEventsGetter('click')
365 };
366
367 function formEventsGetter(type) {
368 return function (el, context, item) {
369 $(el).on(type, function () {
370 context.errHandler = [];
371 parseEachEleCfg(item);
372
373 validating(item, context.errHandler);
374
375 context.handleError();
376 });
377 };
378 }
379
380 /**
381 * 验证器构造器
382 * @param {Object} formInstance 用户自定义规则
383 * @constructor
384 */
385 function Validator(formInstance) {
386 var form = formInstance.form;
387 if (!form) return;
388
389 this.form = form;
390
391 /**
392 [{
393 elem:elem,
394 value: '',
395 type: ''
396 [optional] ,checker: {checker: func, description: ''}
397 }, ..]
398 */
399 this.config = [];
400
401 this.callbackLists = {
402 success: [],
403 failure: []
404 };
405
406 /*
407 this.errHandler;
408 */
409
410 if (formInstance.types) $.extend(VALIDTYPES, formInstance.types);
411
412 this.parsed = false;
413 this.isDefaultPrevented = false;
414 this.ajax = typeof formInstance.ajax === 'boolean' ?
415 formInstance.ajax : true;
416
417 if (formInstance.success) this.on('success', formInstance.success);
418 if (formInstance.failure) this.on('failure', formInstance.failure);
419 if (formInstance.beforeSend) this.beforeSend = formInstance.beforeSend;
420
421 if (formInstance.formElementsEvented) {
422 this.parseConfig();
423 this.parsed = true;
424 this.addFormEvents(this.config);
425 }
426
427 var removeClassFn = function (e) {
428 $(e.target).removeClass('processing');
429 };
430 this.on('success', removeClassFn);
431 this.on('failure', removeClassFn);
432
433 this.submit();
434 }
435
436 // 防止XSS
437 Validator.encodeValue = function (value) {
438 for (var i in ENCODECHAR) {
439 if (ENCODECHAR.hasOwnProperty(i))
440 value = value.replace(new RegExp(i, 'g'), ENCODECHAR[i]);
441 }
442
443 return value;
444 };
445
446 Validator.prototype = {
447 // 为每个表单元素添加事件侦听
448 addFormEvents: function (cfg) {
449 var me = this;
450 var elem, formType, item;
451 for (var i = 0, len = cfg.length; i < len; i++) {
452 item = cfg[i];
453 elem = item.elem;
454 formType = elem.type;
455
456 formEventsHooks[formType](elem, me, item);
457 }
458 },
459 hasErrors: function () {
460 return !!this.errHandler.length;
461 },
462 on: function (type, cb) {
463 if (!this.callbackLists[type]) {
464 throw new Error('no matched event type');
465 }
466
467 this.callbackLists[type] = this.callbackLists[type].concat(
468 Object.prototype.toString.call(cb) === '[object Array]' ?
469 cb : [cb]
470 );
471 },
472 off: function (type) {
473 if (!this.callbackLists[type]) return;
474
475 delete this.callbackLists[type];
476 },
477 emit: function (type, args) {
478 if (!this.callbackLists[type]) {
479 throw new Error('no matched event type');
480 }
481
482 var list = this.callbackLists[type];
483
484 if (type === 'failure' && args && args[0] && args[0].preventDefault) {
485 args[0].preventDefault();
486 }
487
488 for (var i = 0, len = list.length; i < len; i++) {
489 if (typeof list[i] === 'function' && list[i].apply(this.form, args) === false)
490 break;
491 }
492 },
493 submit: function () {
494 var me = this;
495
496 if (!this.form) return;
497
498 $(this.form).on('submit', function (e) {
499 var $this = $(this);
500
501 if ($this.hasClass('processing')) return;
502
503 $this.addClass('processing');
504
505 me.isDefaultPrevented = false;
506 e._preventDefault = e.preventDefault;
507 e.preventDefault = function () {
508 e._preventDefault();
509 me.isDefaultPrevented = true;
510 };
511
512 // 解析配置,parsed为false时,可再次解析
513 if (!me.parsed) {
514 me.parseConfig();
515 me.parsed = true;
516 }
517
518 // 验证
519 me.validate();
520
521 // 验证有错误
522 if (me.hasErrors()) {
523 me.handleError();
524
525 me.emit('failure', [e]);
526 return;
527 }
528
529 // ajax提交默认阻止表单提交
530 if (me.ajax) {
531 e._preventDefault();
532 }
533
534 var def;
535 var form = this;
536
537 /*
538 执行me.beforeSend方法,在成功,提交之前执行,
539 如果返回false就触发失败回调
540 可以返回deferred对象,进行异步操作
541 */
542 if (me.beforeSend && (def = me.beforeSend()) === false) {
543 K.handyWarn({
544 msg: me.beforeSend.errorMsg
545 });
546
547 me.emit('failure', [e]);
548 return;
549 }
550
551 // 如果是deferred对象,序列执行回调
552 if (def && (typeof def.pipe === 'function' || typeof def.then === 'function')) {
553 def = def.pipe || def.then;
554 // 因为是异步操作,必须阻止默认表单提交,与异步提交表单不同
555 if (!e.isDefaultPrevented()) e._preventDefault();
556
557 return def(function () {
558 me.isDefaultPrevented = false;
559 me.emit('success', [e]);
560 // 提交表单
561 if (!me.isDefaultPrevented && !me.ajax) form.submit();
562 }, function () {
563 me.emit('failure', [e]);
564 });
565 } else {
566 me.emit('success', [e]);
567 }
568 });
569 },
570 validate: function () {
571 /**
572 [{
573 elem: elem,
574 msg: ''
575 }, ...]
576 */
577 this.errHandler = [];
578
579 var item;
580
581 // 遍历配置项
582 for (var i = 0, len = this.config.length; i < len; i++) {
583 item = this.config[i];
584
585 if (parseEachEleCfg(item) === false) continue;
586
587 validating(item, this.errHandler);
588 }
589 },
590 // 解析HTML标签中的“data-valid”属性,将有的保存
591 parseConfig: function () {
592 var elems = $('*[data-valid]:not([disabled]):not([readonly])', this.form);
593 var elem, ruler;
594
595 for (var i = 0, len = elems.length; i < len; i++) {
596 elem = elems[i];
597 ruler = elem.getAttribute('data-valid');
598
599 if (ruler)
600 this.config.push({
601 elem: elem,
602 type: ruler
603 });
604 }
605 },
606 // 处理错误
607 handleError: function () {
608 var errs = this.errHandler;
609
610 if (errs.length) {
611 var head = errs.shift();
612 var elem = head.elem;
613
614 K.handyWarn({
615 msg: head.msg,
616 rel: elem,
617 relPos: 'right'
618 });
619
620 if (elem.value) {
621 elem.select();
622 } else {
623 elem.focus();
624 }
625 }
626 }
627 };
628
629 // 验证值,如果不符则保存到错误队列中
630 function validating(item, errHandler) {
631 var checkers = item.checker;
632 var description, checker, value, args, elem;
633
634 for (var i = 0, len = checkers.length; i < len; i++) {
635 checker = checkers[i].checker;
636 description = checkers[i].description;
637 elem = item.elem;
638
639 value = elem[formHooks[elem.type.toLowerCase()] || 'value'];
640
641 // fix IE用value兼容HTML5的placeholder
642 if (elem.getAttribute('placeholder') === value) {
643 value = '';
644 }
645
646 if (value && typeof value === 'string') {
647 value = Validator.encodeValue(value);
648 }
649
650 args = [value].concat(description);
651
652 if (!checker.validate.apply(elem, args)) {
653 errHandler.push({
654 elem: elem,
655 msg: typeof checker.msg === 'function' ? checker.msg.apply(elem, args) : checker.msg
656 });
657 }
658 }
659
660 elem = null;
661 }
662
663 var r_brackets = /^([\w-]+)(?:\(([^)]+)\)|)$/;
664
665 function parseEachEleCfg(item) {
666 if (!(item.checker && item.checker.length)) {
667 var type, description, checker;
668 var types = item.type && item.type.split(r_space) || [];
669
670 if (!types.length) return false;
671
672 // 单个元素可以有多个checker,以空格分隔,且单个checker可有相应的描述语
673 // “charLen(24)”, 括号里面是描述语,
674 // 描述语用在错误信息中
675 item.checker = [];
676 for (var i = 0, len = types.length; i < len; i++) {
677 type = types[i].match(r_brackets);
678 if (!type) continue;
679 checker = VALIDTYPES[type[1]];
680 description = type[2] && type[2].split(',') || [];
681
682 if (!checker) {
683 __console.error('没有相应的验证规则:' + type);
684 continue;
685 }
686
687 item.checker.push({
688 checker: checker,
689 description: description
690 });
691 }
692 }
693
694 return true;
695 }
696 }());
697 </script>
698 </body>
699 </html>