JavaScript设计模式(8)-装饰者模式
装饰者模式
1. 作用:
- 可用来透明地把对象包装在具有同样接口的另一对象之中,这样可以给一个方法添加一些行为,然后将方法调用传递给原始对象。
- 可用于为对象增加功能,用来代替大量子类。
- 装饰者对其组件进行了透明包装,二者可以互换使用,因为他们 实现了同样的接口
2. 例子:自行车
function extend(subClass, superClass) {
var F = function() {}
F.prototype = superClass.prototype;
subClass.prototype = new F();
subClass.prototype.constructor = subClass
subClass.superclass = superClass.prototype
if(superClass.prototype.constructor !== superClass) {
superClass.prototype.constructor = superClass
}
}
// var Bicycle = new Interface('Bicycle', ['assemble', 'wash', 'ride', 'repair', 'getPrice']);
/**
* [AcmeComfortCruiser 自行车类]
*/
var AcmeComfortCruiser = function(){};
AcmeComfortCruiser.prototype = {
assemble: function() {},
wash: function() {},
ride: function() {},
repair: function() {},
getPrice: function() {
return 399.00
}
}
/**
* [BicycleDecorator 装饰类的抽象类]
*/
var BicycleDecorator = function(bicycle) {
// Interface.ensureImplements(bicycle, Bicycle);
this.bicycle = bicycle;
}
BicycleDecorator.prototype = {
assemble: function() {
return this.bicycle.assemble();
},
wash: function() {
return this.bicycle.wash();
},
ride: function() {
return this.bicycle.ride();
},
repair: function() {
return this.bicycle.repair();
},
getPrice: function() {
return this.bicycle.getPrice();
}
}
/**
* [HeadlightDecorator 装饰者类]
*/
var HeadlightDecorator = function(bicycle) {
HeadlightDecorator.superclass.constructor.call(this, bicycle)
}
extend(HeadlightDecorator, BicycleDecorator)
HeadlightDecorator.prototype.assemble = function() {
return this.bicycle.assemble() + ' Attach headlight to handlebars.';
}
HeadlightDecorator.prototype.getPrice = function() {
return this.bicycle.getPrice() + 15.00;
}
/**
* [TaillightDecorator 装饰者类]
*/
var TaillightDecorator = function(bicycle) {
TaillightDecorator.superclass.constructor.call(this, bicycle)
}
extend(TaillightDecorator, BicycleDecorator);
TaillightDecorator.prototype.assemble = function() {
return this.bicycle.assemble() + ' Attach taillight to the seat post.';
}
TaillightDecorator.prototype.getPrice = function() {
return this.bicycle.getPrice() + 9.00;
}
// usage
var myBicycle = new AcmeComfortCruiser();
console.log(myBicycle.getPrice())
myBicycle = new HeadlightDecorator(myBicycle);
console.log(myBicycle.getPrice())
myBicycle = new TaillightDecorator(myBicycle);
console.log(myBicycle.getPrice())
3. 装饰者模式与组合模式的比较
- 相同点:
- 装饰者对象和组合对象都是用来包装别的对象(那些对象组合模式中称为子对象,装饰者模式中称为组件)
- 都实现了同样的接口,并且会把任何调用传递给这些对象
- 差异点:
- 组织模式:用于组织对象;装饰者模式:用于在不修改现有对象及不从其派生子类的前提下为其增添职责。
- 装饰模式的子对象只有一个
4. 装饰者修改其组件的方式
4.1 在方法之后添加行为
先调用组件方法,并在其放回后实施一些附加行为
HeadlightDecorator.prototype.getPrice = function() {
return this.bicycle.getPrice() + 15.00;
}
4.2 在方法之前添加行为
var FrameColorDecorator = function(bicycle, frameColor) {
FrameColorDecorator.superclass.constructor.call(this, bicycle);
// 添加了用以实现其提供的附加特性的属性
this.frameColor = frameColor;
}
extend(FrameColorDecorator, BicycleDecorator);
FrameColorDecorator.prototype.assemble = function() {
// 方法添加的步骤出现在其方法之前
return 'Print the frame ' + this.frameColor + ' and allow it to dry. ' + this.bicycle.assemble()
}
FrameColorDecorator.prototype.getPrice = function() {
return this.bicycle.getPrice() + 30.00;
}
// usage
var myBicycle = new AcmeComfortCruiser();
console.log(myBicycle.assemble())
myBicycle = new FrameColorDecorator(myBicycle, 'red')
console.log(myBicycle.assemble())
4.3 使用替代方法
引入替换组件方法的装饰者后,必须设法确保按正确的顺序应用装饰者(如:使用工厂方法,ps:参考第 5 大点)
/**
* [LifetimeWarrantyDecorator 装饰者类]
*/
var LifetimeWarrantyDecorator = function(bicycle) {
LifetimeWarrantyDecorator.superclass.constructor.call(this, bicycle);
}
extend(LifetimeWarrantyDecorator, BicycleDecorator);
// 把原来的 repair 方法替换为一个新方法,而组件的方法则不会再被调用
LifetimeWarrantyDecorator.prototype.repair = function() {
return 'This bicycle is covered by a lifetime warranty.'
}
LifetimeWarrantyDecorator.prototype.getPrice = function() {
return this.bicycle.getPrice() + 199.00
}
/**
* [TimedWarrantyDecorator 装饰者类]
*/
var TimedWarrantyDecorator = function(bicycle, coverageLengthInYears) {
TimedWarrantyDecorator.superclass.constructor.call(this, bicycle);
this.coverageLength = coverageLengthInYears;
this.expDate = new Date();
var coverageLengthInMs = this.coverageLength * 365 * 24 * 60 * 60 * 1000;
this.expDate.setTime(this.expDate.getTime() + coverageLengthInMs)
}
extend(TimedWarrantyDecorator, BicycleDecorator);
// 根据某种条件决定是否替代组件方法,在条件满足是替代,否则使用组件的方法
TimedWarrantyDecorator.prototype.repair = function() {
var repairInstructions;
var currentDate = new Date();
if(currentDate < this.expDate) {
repairInstructions = 'This bicycle is currently covered by a warrenty.'
}else {
repairInstructions = this.bicycle.repair();
}
return repairInstructions;
}
TimedWarrantyDecorator.prototype.getPrice = function() {
return this.bicycle.getPrice() + (40.00 * this.coverageLength)
}
// usage
var myBicycle = new AcmeComfortCruiser();
console.log(myBicycle.getPrice())
// 替代
myBicycle = new LifetimeWarrantyDecorator(myBicycle)
console.log(myBicycle.repair())
// 判断是否替代
myBicycle = new TimedWarrantyDecorator(myBicycle, 1)
console.log(myBicycle.getPrice())
4.4 添加新方法
想稳妥地实现这一点并不容易,想要使用这些新方法,外围代码必须知道有这些新方法。由于这些新方法并不是在接口中定义的,而是动态添加的,因此有必要进行类型检查,以验明用于包装组件对象的最外层装饰者与用新方法装饰的组件对象相同
var BellDecorator = function(bicycle) {
BellDecorator.superclass.constructor.call(this, bicycle);
}
extend(BellDecorator, BicycleDecorator);
BellDecorator.prototype.assemble = function() {
return this.bicycle.assemble() + 'Attach bell to handlebars.'
}
BellDecorator.prototype.getPrice = function() {
return this.bicycle.getPrice() + 6.00;
}
// 这里添加了一个新方法
BellDecorator.prototype.ringBell = function() {
return 'Bell rung'
}
// usage
var myBicycle = new AcmeComfortCruiser();
myBicycle = new BellDecorator(myBicycle)
myBicycle = new HeadlightDecorator(myBicycle);
console.log(myBicycle.ringBell()) // 这样子会报错,因为 BellDecorator 添加的 ringBell 方法(及其他方法)会在 HeadlightDecorator 类通过 extend() 继承 new F() 时被抹除(也不是被抹除,只是不能在通过当前对象的原型链找到,其实这个方法在新对象的 bicycle 属性里面还是能通过其原型链找到)。
// BellDecorator 必须放在最后应用,否则这个新方法将无法访问。这是因为其他装饰者只能传递他们知道的方法,也即那些定义在接口中的方法。P170
关于上面说到的方法被抹除问题的解决方案
function extend(subClass, superClass) {
var F = function() {}
F.prototype = superClass.prototype;
subClass.prototype = new F();
subClass.prototype.constructor = subClass
subClass.superclass = superClass.prototype
if(superClass.prototype.constructor !== superClass) {
superClass.prototype.constructor = superClass
}
}
var Interface = function(name, methods) {
if(arguments.length !== 2) {
throw new Error("Interface constructor called with "+ arguments.length + "arguments, but expected exactly 2")
}
this.name = name;
this.methods = [];
for(var i=0, len=methods.length; i<len; i++) {
if(typeof methods[i] !== 'string') {
throw new Error("Interface constructor expects method names to be passed in as a string")
}
this.methods.push(methods[i]);
}
}
var Bicycle = new Interface('Bicycle', ['assemble', 'wash', 'ride', 'repair', 'getPrice']);
var BicycleDecorator = function(bicycle) {
this.bicycle = bicycle;
this.interface = Bicycle;
outerloop: // 使用标记,可以在程序的任何地方使用这个名字来引用他
for(var key in this.bicycle) {
if(typeof this.bicycle[key] !== 'function') {
continue outerloop;
}
for(var i=0, len=this.interface.methods.length; i<len; i++) {
if(key === this.interface.methods[i]) {
continue outerloop
}
}
var that = this;
(function(methodName) {
that[methodName] = function() {
return that.bicycle[methodName]();
}
})(key);
}
}
BicycleDecorator.prototype = {
assemble: function() {
return this.bicycle.assemble();
},
wash: function() {
return this.bicycle.wash();
},
ride: function() {
return this.bicycle.ride();
},
repair: function() {
return this.bicycle.repair();
},
getPrice: function() {
return this.bicycle.getPrice();
}
}
/**
* [AcmeComfortCruiser 自行车类]
*/
var AcmeComfortCruiser = function(){};
AcmeComfortCruiser.prototype = {
assemble: function() {
return 'assemble:'
},
wash: function() {},
ride: function() {},
repair: function() {},
getPrice: function() {
return 399.00
}
}
/**
* [HeadlightDecorator 装饰者类]
*/
var HeadlightDecorator = function(bicycle) {
HeadlightDecorator.superclass.constructor.call(this, bicycle)
}
extend(HeadlightDecorator, BicycleDecorator)
HeadlightDecorator.prototype.assemble = function() {
return this.bicycle.assemble() + ' Attach headlight to handlebars.';
}
HeadlightDecorator.prototype.getPrice = function() {
return this.bicycle.getPrice() + 15.00;
}
/**
* [BellDecorator 装饰者类]
*/
var BellDecorator = function(bicycle) {
BellDecorator.superclass.constructor.call(this, bicycle);
}
extend(BellDecorator, BicycleDecorator);
BellDecorator.prototype.assemble = function() {
return this.bicycle.assemble() + 'Attach bell to handlebars.'
}
BellDecorator.prototype.getPrice = function() {
return this.bicycle.getPrice() + 6.00;
}
BellDecorator.prototype.ringBell = function() {
return 'Bell rung'
}
var myBicycle = new AcmeComfortCruiser();
console.log(myBicycle.getPrice())
myBicycle = new BellDecorator(myBicycle)
myBicycle = new HeadlightDecorator(myBicycle);
console.log(myBicycle.getPrice())
console.log(myBicycle.ringBell())
5. 工厂的角色
可以使用工厂模式配合装饰者模式,这样就可以事先规定好实例化时的装饰者应用顺序,从而避免上面说到的新添加的方法在经过别的装饰类包装后访问不到添加的方法的问题
function extend(subClass, superClass) {
var F = function() {}
F.prototype = superClass.prototype;
subClass.prototype = new F();
subClass.prototype.constructor = subClass
subClass.superclass = superClass.prototype
if(superClass.prototype.constructor !== superClass) {
superClass.prototype.constructor = superClass
}
}
var Interface = function(name, methods) {
if(arguments.length !== 2) {
throw new Error("Interface constructor called with "+ arguments.length + "arguments, but expected exactly 2")
}
this.name = name;
this.methods = [];
for(var i=0, len=methods.length; i<len; i++) {
if(typeof methods[i] !== 'string') {
throw new Error("Interface constructor expects method names to be passed in as a string")
}
this.methods.push(methods[i]);
}
}
Interface.ensureImplements = function(object) {
if(arguments.length < 2) {
throw new Error("Function Interface.ensureImplements call with " + arguments.length + "arguments, but expected at least 2")
}
for(var i=1,len=arguments.length; i<len; i++) {
var interface = arguments[i];
if(interface.constructor !== Interface) {
throw new Error("Function Interface.ensureImplements expects arguments two and above to be instances of Interface");
}
for(var j=0, methodsLen = interface.methods.length; j<methodsLen; j++) {
var method = interface.methods[j];
if(!object[method] || typeof object[method] !== 'function') {
throw new Error('Function Interface.ensureImplements: Object does not implement the '+ interface.name + " interface. Method " + method + " was not found")
}
}
}
}
var Bicycle = new Interface('Bicycle', ['assemble', 'wash', 'ride', 'repair', 'getPrice']);
// model 1
var AcmeComfortCruiser = function(){};
AcmeComfortCruiser.prototype = {
assemble: function() {},
wash: function() {},
ride: function() {},
repair: function() {},
getPrice: function() {
return 399.00
}
}
// model 2
var AcmeSpeedster = function() {}
extend(AcmeSpeedster, AcmeComfortCruiser)
/**
* [BicycleDecorator 装饰类的抽象类]
*/
var BicycleDecorator = function(bicycle) {
// Interface.ensureImplements(bicycle, Bicycle);
this.bicycle = bicycle;
}
BicycleDecorator.prototype = {
assemble: function() {
return this.bicycle.assemble();
},
wash: function() {
return this.bicycle.wash();
},
ride: function() {
return this.bicycle.ride();
},
repair: function() {
return this.bicycle.repair();
},
getPrice: function() {
return this.bicycle.getPrice();
}
}
/**
* [HeadlightDecorator 装饰者类]
*/
var HeadlightDecorator = function(bicycle) {
HeadlightDecorator.superclass.constructor.call(this, bicycle)
}
extend(HeadlightDecorator, BicycleDecorator)
HeadlightDecorator.prototype.assemble = function() {
return this.bicycle.assemble() + ' Attach headlight to handlebars.';
}
HeadlightDecorator.prototype.getPrice = function() {
return this.bicycle.getPrice() + 15.00;
}
/**
* [BellDecorator 装饰者类]
*/
var BellDecorator = function(bicycle) {
BellDecorator.superclass.constructor.call(this, bicycle);
}
extend(BellDecorator, BicycleDecorator);
BellDecorator.prototype.assemble = function() {
return this.bicycle.assemble() + 'Attach bell to handlebars.'
}
BellDecorator.prototype.getPrice = function() {
return this.bicycle.getPrice() + 6.00;
}
BellDecorator.prototype.ringBell = function() {
return 'Bell rung'
}
// BicycleShop class 是一个抽象类,需要在继承后实现里面的方法
var BicycleShop = function() {};
BicycleShop.prototype = {
sellBicycle: function(model) {
var bicycle = this.createBicycle(model)
bicycle.assemble()
return bicycle;
},
// 工厂方法
createBicycle: function(model) {
throw new Error('Unsupported operation on an abstract class.')
}
}
var AcmeBicycleShop = function() {};
extend(AcmeBicycleShop, BicycleShop);
AcmeBicycleShop.prototype.createBicycle = function(model, options) {
var bicycle = new AcmeBicycleShop.models[model]();
// 有必要时可以在这里对装饰者组件先后应用进行排序,下面使用的是直接遍历按顺序应用
for(var i=0, len= options.length; i<len; i++) {
var decorator = AcmeBicycleShop.options[options[i].name]
if(typeof decorator !== 'function') {
throw new Error('Decorator ' + options[i].name + 'not found');
}
var argument = options[i].arg;
bicycle = new decorator(bicycle, argument)
}
Interface.ensureImplements(bicycle, Bicycle);
return bicycle
}
AcmeBicycleShop.models = {
'The Speedster' : AcmeSpeedster,
'The Comfort Cruiser' : AcmeComfortCruiser
}
AcmeBicycleShop.options = {
'headlight' : HeadlightDecorator,
'bell': BellDecorator
}
var alecsCruisers = new AcmeBicycleShop();
var myBicycle = alecsCruisers.createBicycle('The Speedster', [
{name: 'headlight'},
{name: 'bell'}
])
myBicycle.ringBell()
6. 函数装饰者
用于包装独立的函数和方法的装饰者
6.1 包装函数
// 将传入函数的执行结果转化为大写形式
function upperCaseDecorator(func) {
return function() {
return func.apply(this, arguments).toUpperCase()
}
}
function getDate() {
return (new Date()).toString()
}
getDateCaps = upperCaseDecorator(getDate)
// usage
getDateCaps()
6.2 包装方法
function upperCaseDecorator(func) {
return function() {
return func.apply(this, arguments).toUpperCase()
}
}
function extend(subClass, superClass) {
var F = function() {}
F.prototype = superClass.prototype;
subClass.prototype = new F();
subClass.prototype.constructor = subClass
subClass.superclass = superClass.prototype
if(superClass.prototype.constructor !== superClass) {
superClass.prototype.constructor = superClass
}
}
/**
* [AcmeComfortCruiser 自行车类]
*/
var AcmeComfortCruiser = function(){};
AcmeComfortCruiser.prototype = {
assemble: function() {},
wash: function() {},
ride: function() {},
repair: function() {},
getPrice: function() {
return 399.00
}
}
/**
* [BicycleDecorator 装饰类的抽象类]
*/
var BicycleDecorator = function(bicycle) {
this.bicycle = bicycle;
}
BicycleDecorator.prototype = {
assemble: function() {
return this.bicycle.assemble();
},
wash: function() {
return this.bicycle.wash();
},
ride: function() {
return this.bicycle.ride();
},
repair: function() {
return this.bicycle.repair();
},
getPrice: function() {
return this.bicycle.getPrice();
}
}
/**
* [BellDecorator 装饰者类]
*/
var BellDecorator = function(bicycle) {
BellDecorator.superclass.constructor.call(this, bicycle);
}
extend(BellDecorator, BicycleDecorator);
BellDecorator.prototype.ringBell = function() {
return 'Bell rung'
}
// 使用函数装饰者装饰方法
BellDecorator.prototype.ringBellLoudly = upperCaseDecorator(BellDecorator.prototype.ringBell)
var myBicycle = new AcmeComfortCruiser();
myBicycle = new BellDecorator(myBicycle)
myBicycle.ringBell()
myBicycle.ringBellLoudly()
7. 装饰者模式的适用场合
- 为类添加特效或职能,而从该类派生子类又不实际时(不实际可能是子类的数量大)
- 需要为对象添加特效而又不想改变使用该对象的代码的话,也可使用装饰者模式。因为装饰者模式可以动态而透明的修改对象,所以它们很适合于修改现有系统这一任务。
8. 示例:方法性能分析器
/**
* [ListBuilder]
* @param {[type]} parent [description]
*/
var ListBuilder = function(parent) {
this.parentEl = document.getElementById(parent);
}
ListBuilder.prototype = {
buildList: function(listLength) {
var list = document.createElement('ol');
this.parentEl.appendChild(list);
for(var i=0; i< listLength; i++) {
var item = document.createElement('li');
list.appendChild(item)
}
}
}
/**
* [MethodProfiler class]
* @param {[type]} component [description]
*/
var MethodProfiler = function(component) {
this.component = component;
this.timers = {};
for(var key in this.component) {
if(typeof this.component[key] !== 'function') {
continue;
}
var that = this;
// 使用匿名函数的作用是保留正确的 methodName 变量值
(function(methodName) {
that[methodName] = function() {
that.startTimer(methodName);
var returnValue = that.component[methodName].apply(that.component, arguments);
that.displayTime(methodName, that.getElapsedTime(methodName));
return returnValue;
}
})(key)
}
}
MethodProfiler.prototype = {
startTimer: function(methodName) {
this.timers[methodName] = (new Date()).getTime();
},
getElapsedTime: function(methodName) {
return (new Date()).getTime() - this.timers[methodName];
},
displayTime: function(methodName, time) {
console.log(methodName + ': ' + time + ' ms')
}
}
// usage
var list = new ListBuilder('feed-readers')
var listp = new MethodProfiler(list)
listp.buildList(500)
9. 装饰者的利与弊
- 利:
- 方便,灵活,透明
- 不用重新定义对象就能对其进去扩充
- 弊:
- 在遇到用装饰者包装起来的对象时,那些依赖于类型检查的代码会出问题。
- 使用装饰者模式往往会增加架构的复杂度。(添加了一些小对象;实现动态接口的装饰者涉及的语法细节也令人生畏)
注意
转载、引用,但请标明作者和原文地址
JavaScript设计模式(8)-装饰者模式的更多相关文章
- 再起航,我的学习笔记之JavaScript设计模式13(装饰者模式)
装饰者模式 装饰者模式(Decorator): 在不改变原对象的基础上,通过对其进行过包装拓展(添加属性高或者方法)使原有对象可以满足用户的更复杂需求. 如果现在我们有个需求,需要做一个提交表单,当我 ...
- Javascript设计模式之装饰者模式详解篇
一.前言: 装饰者模式(Decorator Pattern):在不改变原类和继承的情况下动态扩展对象功能,通过包装一个对象来实现一个新的具有原对象相同接口的新的对象. 装饰者模式的特点: 1. 在不改 ...
- JavaScript设计模式-17.装饰者模式(下)
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- JavaScript设计模式-16.装饰者模式(上)
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- JavaScript设计模式(装饰者模式)
一.模拟传统面向对象语言的装饰者模式: 假设我们在编写一个飞机大战的游戏,随着经验值的增加,我们操作的飞机对象可以升级成更厉害的飞机,一开始这些飞机只能发射普通的子弹,升到第二级时可以发射导弹,升到第 ...
- javascript设计模式之装饰者模式
/* * 装饰者模式提供比继承更有弹性的替代方案 * 在不改变原构造函数的情况下,添加新的属性或功能 */ //需要装饰的类(函数) function Macbook() { this.cost = ...
- 学习javascript设计模式之装饰者模式
1.装饰者模式定义:给对象动态添加职责的方式称为装饰者(decorator)模式. js如何实现装饰者模式 通过保存原函数引用方式改写某函数 window.onload = function(){al ...
- Java 设计模式泛谈&装饰者模式和单例模式
设计模式(Design Pattern) 1.是一套被反复使用.多人知晓的,经过分类编目 的 代码设计经验总结.使用设计模式是为了可重用代码,让代码更容易维护以及扩展. 2.简单的讲:所谓模式就是得到 ...
- C#设计模式(9)——装饰者模式(Decorator Pattern)
一.引言 在软件开发中,我们经常想要对一类对象添加不同的功能,例如要给手机添加贴膜,手机挂件,手机外壳等,如果此时利用继承来实现的话,就需要定义无数的类,如StickerPhone(贴膜是手机类).A ...
随机推荐
- Windows下快速建立cocos2d-x项目
准备工作 1.根据当前系统版本,下载对应版本的Python 32位下载地址:http://www.python.org/ftp/python/2.7.5/python-2.7. ...
- [Python爬虫] Selenium爬取新浪微博客户端用户信息、热点话题及评论 (上)
转载自:http://blog.csdn.net/eastmount/article/details/51231852 一. 文章介绍 源码下载地址:http://download.csdn.net/ ...
- ubuntu 开发板ping通虚拟机挂载nfs服务器
先.nfs服务配置1.设置开发板ip ,同一网段2.开发板上操作:ifconfig eth0 192.168.1.203.测试是否能够ping通:ping 192.168.1.194.测试开发板ip是 ...
- Windows Server 2016-重命名域控制器
当公司发展到一定规模或者信息化建设到一定程度的情况下,很多信息化规范出台:很多初期服务器搭建包括服务搭建等计算机名等都是按照默认或者随机命名的,不便于区分业务或服务等.通过前边的章节我们对Active ...
- UML设计
UML设计 1. UML的概念 Unified Modeling Language(UML)又称统一建模语言或标准建模语言,是一个支持模型化和软件系统开发的图形化语言.为软件开发的所有阶段提供模型化和 ...
- 关于Apache配置虚拟主机后在局域网中让其他电脑访问
#-----------adxssp------------# NameVirtualHost *:80 <VirtualHost *:80> ServerName www.b.com D ...
- Thymeleaf入门基础
一.简介 1.thymeleaf优点 ①是一个支持html原型的自然引擎,它在html标签增加额外的属性来达到模板+数据的展示方式,由于浏览器解释html时,忽略未定义的标签属性,因此thymelea ...
- Hibernate学习(二)保存数据
package cn.lonecloud.test; import java.util.Date; import org.hibernate.HibernateException; import or ...
- MIPS中有关于分支指令及跳转寻址
分支指令 分支指令包含该指令,和两个操作数,以及跳转的分支地址,该地址是相对于下一条指令的相对地址 分支指令占6位 操作数1占5位 操作数2占5位 分支指令16位 例如 bne ...
- 用Composer获取第三方资源总是失败咋办?
凉拌!!! 不不不,哥可是一个有追求的人,没那么容易放弃的! 所以我选择用中国全量镜像,https://pkg.phpcomposer.com/ 使用方法: 对,就是命令行方法,我最喜欢的方法!!! ...