S.O.L.I.D五大原则- 深入了解javascript
/*
S.O.L.I.D五大原则
1. 单一原则
2. 开闭原则
3. 里氏替换原则
4. 接口分离原则
5. 依赖反转原则
*/
/*单一原则:类发生更改的原因只有一个
如何知道一个对象的多个行为构造多个职责或单个职责?
判断标准:
1. 存储对象:提供对象信息给其他对象
2. 维护对象:维护对象和信息之间的关系
3. 服务对象:处理工作并提供服务给其他对象
4. 控制对象:控制决策一系列负责的任务处理
5. 协调对象:不做处理工作,只是delegate工作到其他对象
5. 接口对象:在系统各个部分转化信息(或请求)
*/
//商品实体,有ID和描述
function Product(id, desc) {
this.getId = function () {
return id;
}
this.getDesc = function () {
return desc;
}
}
//购物车,添加清单
function Cart(evetAggregator) {
var items = [];
this.addItem = function (item) {
items.push(item);
}
}
(function () {
//初始化一系列商品
var products = [new Product(1, "a"), new Product(2, "b"), new Product(3, "c")],
cart = new Cart();
//商品双击后获取ID,将商品添加到CART中
function addToCart() {
var productId = $(this).attr('id');
var product = $.grep(products, function (x) {
return x.getId() == productId;
})[0];
cart.addItem(product); var newItem = $('<li></li>').html(product.getDesc()).attr('id-cart', product.getId()).apendTo('#cart');
}
//将商品遍历,初始化事件,添加到商品集合中
products.forEach(function (prodcuct) {
var newItem = $('<li></li>').html(product.getDesc()).attr('id', product.getId()).dbclick(addToCart).apendTo("#products");
});
}());
/*
如上,职责分析
1. 有products的声明
2. 将product集合绑定到#products中,同时添加购物车事件处理
3. 有购物车展示功能
4. 添加产品到购物车并显示功能
*/
/*
采用事件聚合理论(event aggreagator)优化,分为2部分
1. Event,用于Handler回调代码
2. EventAggregator,订阅和发布事件
*/
function Event(name) {
var handlers = [];
this.getName = function () {
return name;
};
this.addHandler = function (handler) {
handlers.push(handler);
};
this.removeHandler = function (handler) {
for (var i = 0; i < handlers.length; i++) {
if (handlers[i]==handler) {
handlers.slice(i, 1);
break;
}
}
};
this.fire = function (eventArgs) {
handlers.forEach(function (h) {
h(eventArgs);
});
}
} function EventAggregator() {
var events = [];
function getEvent(eventName) {
return $.grep(events, function (x) {
return event.getName() == eventName;
})[0];
}
this.publish = function (eventName, eventArgs) {
var event = getEvent(eventName); if (!event) {
event = new Event(eventName);
events.push(event);
}
event.fire(eventArgs);
}
this.subscribe = function (eventName,handler) {
var event = getEvent(eventName);
if (!event) {
event = new Event(eventName);
events.push(event);
}
event.addHandler(handler);
}
}
//修改Cart
function Cart(eventAggregator) {
var items = [];
this.addItem = function (item) {
items.push(item);
eventAggregator.publish('itemAdd', item);
}
}
//新增CartController,接收cart对象的事件聚合器,通过订阅itemAdd来增加一个li节点,通过订阅productSelected事件来添加product
function CartController(cart, eventAggregator) {
eventAggregator.subscribe('itemAdd', function(eventArgs) {
var newItem = $('<li></li>').html(eventArgs.getDesc()).attr('id-cart', eventArgs.getId()).appendTo("#cart");
});
eventAggregator.subscribe("productSelected", function (eventArgs) { cart.addItem(eventArgs.product); });
}
//新增repository:ajax获取数据,暴露get方法
function ProductRepository() {
var products = [new Product(1, "a"), new Product(2, "b"), new Product(3, "c")];
this.getProducts = function () {
return products;
}
}
//新增ProductController: 定义OnProductSelect方法,主要发布触发ProductSelected事件,forEach主要用于绑定数据到产品列表上
function ProductController(eventAggregator,productRepository) {
var products = productRepository.getProducts();
function onProductSelected() {
var productId = $(this).attr('id');
var product = $.grep(products, function (x) {
return x.getId() == productId;
})[0];
eventAggregator.publish('productSelected', { product: product });
}
products.forEach(function (product) {
var newItem = $('<li></li>').html(product.getDesc()).attr('id', product.getId()).dblclick(onProductSelected).appendTo("#products");
});
}
//最后声明匿名函数
(function () {
var eventAggregator = new EventAggregator(),
cart = new Cart(eventAggregator),
cartController = new CartController(cart, eventAggregator),
productRepository = new ProductRepository(),
productController = new ProductController(eventAggregator, productRepository); }()); /*
开闭原则:软件开发对外扩展开发,对内修改关闭,即在不修改的前提下扩展
*/
//一、问题代码
//问题类型
var AnswerType = {
Choice: 0,
Inut: 1
}
//定义问题实体
function question(label, answerType, choices) {
return {
label: label,
answerType: answerType,
choices: choices
};
}
var view = (function () {
function renderQuestion(target, question) {
//创建最外层DIV
var questionWrapper = document.createElement("div");
var answer = document.createElement('div');
//当问题为下拉选择模式
if (question.answerType == AnswerType.Choice) {
var input = document.createElement('select');
var len = question.choices.length;
for (var i = 0; i < len; i++) {
var option = document.createElement('option');
option.text = question.choices[i];
option.value = question.choices[i];
input.appendChild(option);
}
}
else if (question.answerType == AnswerType.Inut) {
var input = document.createElement('input'); input.type = 'text';
}
answer.appendChild(input);
questionWrapper.appendChild(questionLabel);
questionWrapper.appendChild(answer);
target.appendChild(questionWrapper);
}
// 遍历所有的问题列表进行展示
return {
rendeer: function (target, questions) {
for (var i = 0; i < questions.length; i++) {
renderQuestion(target, questions[i]);
}
}
}
}());
/*
问题代码分析:
违背开闭原则,如果增加一个question类型,则需要再次修改renderQuestion函数
*/
//choiceQuestionCreator函数和inputQuestionCreator函数分别对应下拉菜单和input输入框的renderInput实现,通过内部调用统一的questionCreator(spec, my)然后返回that对象(同一类型哦)。
function questionCreator(spec, my) {
var that = {};
my = my || {};
my.label = spec.label;
my.renderInput = function () {
throw "not implemented";
}
that.render = function (target) {
var questionWrapper = document.createElement("div");
var answer = my.renderInput();
questionWrapper.appendChild(answer);
return questionWrapper;
}
return that;
}
function choiceQuestionCreator(spec, my) {
var my = {},
that = questionCreator(spec, my);
//实现renderInput
my.renderInput = function () {
var input = document.createElement('select');
var len = spec.choices.length;
for (var i = 0; i < len; i++) {
var option = document.createElement('option');
option.text = spec.choices[i];
option.value = spec.choices[i];
input.appendChild(option);
}
return input;
}
return that;
}
function inputQuestionCreator(spec, my) {
var my = {},
that = questionCreator(spec, my);
// input类型的renderInput实现
my.renderInput = function () {
var input = document.createElement('input');
input.type = 'text';
return input;
};
return that;
}
var view = {
render: function (target, questions) {
for (var i = 0; i < questions.length; i++) {
target.appendChild(question[i].render());
}
}
}
var question = [
choiceQuestionCreator({ label: '', choices: ['yes', 'no'] }),
inputQuestionCreator({ label: '' })
];
var questionRegion = document.getElementById('questions');
view.render(questionRegion, questions);
/*
1. 首先,questionCreator采用模板方法模式将处理问题的功能delegate给每个问题类型扩展代码renderInput上
2. 其次,用spec替换替换label、choices,防止属性暴露给外部代码
3. 然后,每个实现都必须含有renderInput覆盖原有的questionCreator方法里的renderInput代码:策略模式
*/ /*
里氏替换原则:派生类必须可以替换他的基类型
*/
//一、定义Vehicle函数,提供基本操作
function Vehicle(my) {
var my = my || {};
my.speed = 0;
my.running = false;
this.speed = function () {
return my.speed;
}
this.start = function () {
my.running = true;
}
this.stop = function () {
my.running = false;
}
this.accelerate = function() {
my.speed++;
};
this.decelerate = function () {
my.speed--;
}
return my;
}
//FastVehicle违背里氏替换原则因为加减速的数据不一样。子类已非等价于基类的期望行为
function FastVehicle(my) {
var my = my || {};
var that = new Vehicle(my);
that.accelerate = function () {
my.speed += 3;
};
return that;
}
/*
二、减少LSP妨碍
1. 使用契约
a.检查使用TDD来指导代码设计
b.设计可重用类库时候可随意使用契约设计技术
2. 避免继承,多组合
*/
// 三、与行为有关,而不是继承
// LSP本质是:行为兼容性,并非继承 /*
接口隔离原则 Interface Segregation Principle
不应该强迫用户依赖于他们不同的方法
*/
// 一、JS没有接口的概念,但可以当做一个contract提供约束
var myBindApply = {};
myBindApply.modelObserver = (function () {
//私有变量
return {
observe: function (model) {
/*代码*/
return newModel
},
onChange: function (callback) {
/*代码*/
}
};
}());
myBindApply.viewAdaptor = (function () {
return {
bind: function (model) { }
};
}());
//公共接口是bind方法
myBindApply.bind = function (model) {
/*私有变量*/
myBindApply.modelObserver.onChange(function () { });
var vm = myBindApply.modelObserver.observe(model);
myBindApply.viewAdaptor.bind(vm);
}; /*
依赖倒置原则 Dependency Inversion Principle
高层模块不依赖于底层模块,二者应该依赖于抽象。
抽象不依赖于细节,细节依赖于抽象
*/
/*
举例:传统架构中,高层模块(封装了核心业务逻辑)总依赖一些基础点模块 ==》 依赖倒置 ==》底层模块依赖高层模块定义的接口
传统程序持久化依赖于持久化模块API ==》 依赖倒置 ==》 核心模块需要定义持久化API接口,然后持久化模块需要实现这个API接口
*/
//在JavaScript里,依赖倒置原则的适用性仅仅限于高层模块和低层模块之间的语义耦合,比如,DIP可以根据需要去增加接口而不是耦合低层模块定义的隐式接口。
$.fn.traceMap = function (options) {
var defaults = {};
options = $.extend({}, defaults, options); var mapOptions = {
center: new google.maps.LatLng(options.latitude, options.longitude),
zoom: 12
},
map = new google.maps.Map(this[0], mapOptions); options.fee.update(function () {
map.setCenter();
});
return this;
};
var updater = (function () {
return {
update: function (callback) {
updateMap = callback;
}
};
})();
$("#map_canvas").trackMap({ feed: updater });
//trackMap函数有2个依赖:第三方的Google Maps API和Location feed //解决方案:消除MAP依赖,提供trackMap.googleMapsProvider隐式接口(抽象出地图提供商provider接口)
$.fn.trackMap = function (options) {
var defaults = {
/* defaults */
};
options = $.extend({}, defaults, options);
options.provider.showMap(this[0], options.latitude, options.longitude, options.icon, options.title);
options.feed.update(
function (latitude, longitude) {
options.provider.updateMap(latitude, longitude);
});
return this;
};
$("#map_canvas").trackMap(
{
latitude: 35.044640193770725,
longitude: -89.98193264007568,
icon: 'http://bit.ly/zjnGDe',
title: 'Tracking Number: 12345',
feed: updater,
provider: trackMap.googleMapsProvider
});
//配合MaP API的实现对象
trackMap.googleMapsProvider =
(function () {
var marker, map;
return {
showMap: function (element, latitude, longitude, icon, title) {
var mapOptions = {
center: new google.maps.LatLng(latitude, longitude),
zoom: 12,
mapTypeId: google.maps.MapTypeId.ROADMAP
}, pos = new google.maps.LatLng(latitude, longitude);
map = new google.maps.Map(element, mapOptions);
marker = new google.maps.Marker({ position: pos, title: title, icon: icon });
marker.setMap(map);
},
updateMap: function (latitude, longitude) {
marker.setMap(null);
var newLatLng = new google.maps.LatLng(latitude, longitude);
marker.position = newLatLng; marker.setMap(map);
map.setCenter(newLatLng);
}
};
})();
/*
何时依赖注入?
依赖注入是控制反转的一个特殊形式,反转的意思一个组件如何获取它的依赖。
依赖注入的意思就是:依赖提供给组件,而不是组件去获取依赖,
意思是创建一个依赖的实例,通过工厂去请求这个依赖,通过Service Locator或组件自身的初始化去请求这个依赖。
依赖倒置原则和依赖注入都是关注依赖,并且都是用于反转。不过,依赖倒置原则没有关注组件如何获取依赖,而是只关注高层模块如何从低层模块里解耦出来。
某种意义上说,依赖倒置原则是控制反转的另外一种形式,这里反转的是哪个模块定义接口(从低层里定义,反转到高层里定义)。 */
S.O.L.I.D五大原则- 深入了解javascript的更多相关文章
- S.O.L.I.D五大原则之单一职责SRP
转自 : 汤姆大叔的blog Bob大叔提出并发扬了S.O.L.I.D五大原则,用来更好地进行面向对象编程,五大原则分别是: The Single Responsibility Principle(单 ...
- 深入理解JavaScript系列(22):S.O.L.I.D五大原则之依赖倒置原则DIP
前言 本章我们要讲解的是S.O.L.I.D五大原则JavaScript语言实现的第5篇,依赖倒置原则LSP(The Dependency Inversion Principle ). 英文原文:htt ...
- 深入理解JavaScript系列(21):S.O.L.I.D五大原则之接口隔离原则ISP
前言 本章我们要讲解的是S.O.L.I.D五大原则JavaScript语言实现的第4篇,接口隔离原则ISP(The Interface Segregation Principle). 英文原文:htt ...
- 深入理解JavaScript系列(8):S.O.L.I.D五大原则之里氏替换原则LSP
前言 本章我们要讲解的是S.O.L.I.D五大原则JavaScript语言实现的第3篇,里氏替换原则LSP(The Liskov Substitution Principle ). 英文原文:http ...
- 深入理解JavaScript系列(7):S.O.L.I.D五大原则之开闭原则OCP
前言 本章我们要讲解的是S.O.L.I.D五大原则JavaScript语言实现的第2篇,开闭原则OCP(The Open/Closed Principle ). 开闭原则的描述是: Software ...
- 深入理解JavaScript系列(6):S.O.L.I.D五大原则之单一职责SRP
前言 Bob大叔提出并发扬了S.O.L.I.D五大原则,用来更好地进行面向对象编程,五大原则分别是: The Single Responsibility Principle(单一职责SRP) The ...
- 面向对象设计SOLID五大原则
转载自:码农社区,http://w3croom.com/read.php?tid-4522.html 今天我给大家带来的是面向对象设计SOLID五大原则的经典解说. 我们知道,面向对象对于 ...
- Java成长第五集--面向对象设计的五大原则
S.O.L.I.D 是面向对象设计(OOD)和面向对象编程(OOP)中的几个重要编码原则(Programming Priciple)的首字母缩写.以下图说明: 下面就个人的理解来说说这五大原则的含义到 ...
- OO的五大原则是指SRP、OCP、LSP、DIP、ISP。
OO的高层原则,面向对象设计的基本原则 设计模式之六大原则--开闭原则(OCP) 设计模式之六大原则--迪米特法则(LoD,LKP) 设计模式之六大原则--接口隔离原则(ISP) 设计模式之六大原则- ...
随机推荐
- (转)Android之接口回调机制
开发中,接口回调是我们经常用到的. 接口回调的意思即,注册之后并不立马执行,而在某个时机触发执行. 举个例子: A有一个问题不会,他去问B,B暂时解决不出来,B说,等我(B)解决了再告诉你(A)此时A ...
- Chromium浏览器高级开发系列第一篇:如何获取最新chromium源码
背景: 最近摊上一个事儿,领导非要让写一篇技术文章,思来想去,自己接触chrome浏览器时间也不短了,干脆就总结一下吧.于是乎,本文顺理成章.由于有些细节必需描述清楚,所以这次先讲如何拿到ch ...
- 【学习笔记】【C语言】逻辑运算符
有时候,我们需要在多个条件同时成立的时候才能执行某段代码,比如:用户只有同时输入了QQ和密码,才能执行登录代码,如果只输入了QQ或者只输入了密码,就不能执行登录代码.这种情况下,我们就要借助于C语言提 ...
- (转)Yale CAS + .net Client 实现 SSO(6)
第一部分:安装配置 Tomcat 第二部分:安装配置 CAS 第三部分:实现 ASP.NET WebForm Client 第四部分:实现基于数据库的身份验证 第五部分:扩展基于数据库的身份验证 第六 ...
- mysql之触发器trigger(1)
触发器(trigger):监视某种情况,并触发某种操作. 触发器创建语法四要素:1.监视地点(table) 2.监视事件(insert/update/delete) 3.触发时间(after/befo ...
- 去掉影响效率的where 1=1
最近看了篇文章,觉得挺有道理.实际项目中,我们进行sql条件过滤,我们不能确定是不是有条件.也不能确定条件的个数.大多数人会先把sql语句组装为: 这样,如果有其他过滤条件直接加上“and 其他条件” ...
- C++虐恋:MBCS安装失败导致的四天误工
情况描述:接收远程队友的C++代码,基于vc120工具集(VS2013),而我的机器上是VS2015,需要安装VS2013(只选MFC,除主程序与MFC外其余的组件全部卸掉).然后开始编译,提示 MS ...
- IEEE 802.15.4协议学习之MAC层
MAC负责建立于网络的同步,支持关联和取消关联.MAC层的安全以及控制物理信道访问机制.信道访问机制主要有以下几种: 1. 有序的物理无线信道访问机制 2. 协调器启动和维 ...
- Python's Exception 层级结构
BaseException +-- SystemExit +-- KeyboardInterrupt +-- GeneratorExit +-- Exception +-- StopIteration ...
- no permissions fastboot
no permissions fastboot 获取fastboot文件 1.编译后得android源码会在目录: andsource2/out/host/linux-x86/bin 产生fastb ...