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 ... 
随机推荐
- POJ 2826 An Easy Problem?![线段]
			An Easy Problem?! Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 12970 Accepted: 199 ... 
- NSData 与 NSString 的转化
			1 NSString * str = @"hello, world!"; 2 NSData * data = [str dataUsingEncoding:NSUTF8String ... 
- LNMP搭建03 -- 编译安装PHP
			[编译安装PHP] 为了顺利安装PHP,先安装下面这些: [CentOS 编译 PHP 前要做的事情] yum install -y gcc gcc-c++ make zlib zlib-deve ... 
- H5 拖拽,一个函数搞定,直接指定对象设置可拖拽
			页面上,弹个小窗体,想让它可以拖拽,又不想 加载一堆js,就简单的能让他可以拖动? 嗯,下面有这样一个函数,调用下就好了! 1. 先来说说 H5的 拖拽 在 HTML5 中,拖放是标准的一部分,任何元 ... 
- NDK开发,如何配置 debug环境
			刚开始做NDK 开发的时候,Android Studio 还没提供了 native C/C++ 设置断点 调试,我们都是通过输出 日志来调试,这样费时耗力.Android Studio 应该是在 2. ... 
- Java经典编程题50道之十三
			一个整数,它加上100后是一个完全平方数,再加上168又是一个完全平方数,请问该数是多少? public class Example13 { public static void main(St ... 
- Create小程序
			我有时候喜欢直接用命令行创建.编译.执行java文件, 每次创建一个文件都要新建一个.java文件,然后再编辑.java文件加入类名,主函数…… 这些流程我有点厌倦,于是就编写了一个超级简单的自动创建 ... 
- DAY2-JAVA
			2018-1-28学习笔记 1.在开发中定义类.方法时也可以先添加文档注释,再用javadoc工具生成自己的API文档.Java文档注释和java工具使用. 2.掌握查阅java JDK和API文档. ... 
- HDU - 1248 寒冰王座 数学or暴力枚举
			思路: 1.暴力枚举每种面值的张数,将可以花光的钱记录下来.每次判断n是否能够用光,能则输出0,不能则向更少金额寻找是否有能够花光的.时间复杂度O(n) 2.350 = 200 + 150,买350的 ... 
- A.Alyona and copybooks
			这题考查思维的全面性. 一开始我直接分类推公式,余数不同分类讨论. AC代码: #include<cstdio> #include<algorithm> using names ... 
