// Create and add second product: Blink/20420101
var engine = new UserAgent.Product('Blink', '20420101');
userAgent.addProduct(engine);
// EvilCorpBrowser/1.2 (X11; Linux; en-us) Blink/20420101
userAgent.toString();
// Make some more changes to engine product
engine.setComment('Hello World');
// EvilCorpBrowser/1.2 (X11; Linux; en-us) Blink/20420101 (Hello World)
userAgent.toString();
根据你的代码的复杂度,你可能会在组织结构上花费一些时间。利用设计模式是组织你的代码库的好办法,甚至可以解决一些技术问题。这还能避免加入新特性带来的大面积重构。
##灵活性和定制性
灵活性是让代码库变得强大的要素,但是确定可以定制与不能定制的界限是很困难的。 chart.js 和 D3.js 是很好的例子。这两个代码库都是用来进行数据可视化的。Chart.js可以很简单的创建不同形式的内置图表。但是如果你想对图像进行更多的掌控,D3.js才是你需要的。
有好几种方法可以把控制权交给用户:配置,暴露公共方法,通过回调和事件。
配置代码库通常在初始化之前完成。但是一些代码库允许尼在运行时对配置进行修改。配置通常被细小的部分限制,只有为了之后的使用而修改它们的数值才是被允许的。
// Configure at initialization
var userAgent = new UserAgent({
commentSeparator: ';'
});
// Run-time configuration using a public method
userAgent.setOption('commentSeparator', '-');
// Run-time configuration using a public property
userAgent.commentSeparator = '-';
方法通常是暴露给实例使用的,比如说从实例中获取数据,或者设置实例的数据和执行操作。
var userAgent = new UserAgent;
// A getter to retrieve comments from all products
userAgent.getComments();
// An action to shuffle the order of all products
userAgent.shuffleProducts();
回调通常是在公共的方法中被传递的,通常在异步操作后执行用户的代码。
var userAgent = new UserAgent;
userAgent.doAsyncThing(function asyncThingDone() {
// Run code after async thing is done
});
事件有很多种可能。有点像回调,除了增加事件句柄是不应该触发操作的。事件通常用于监听,你可能会猜到,这可是事件!更像回调的是,你可以提供更多的信息和返回一个数值给代码库去进行操作。
var userAgent = new UserAgent;
// Validate a product on addition
userAgent.on('product.add', function onProductAdd(e, product) {
var shouldAddProduct = product.toString().length < 5;
// Tell the library to add the product or not
return shouldAddProduct;
});
在一些例子中,你可能允许用户对你的代码库进行扩展。因此,你需要暴露一些公共方法或者属性来让用户填充,像Angular的模块 ( angular.module('myModule'))和Jquery的 fn ( jQuery.fn.myPlugin )或者什么都不做,只是简单的让用户获取你的代码库的命名空间:
// AngryUserAgent module
// Has access to UserAgent namespace
(function AngryUserAgent(UserAgent) {
// Create new method .toAngryString()
UserAgent.prototype.toAngryString = function() {
return this.toString().toUpperCase();
};
})(UserAgent);
// Application code
var userAgent = new UserAgent;
// ...
// EVILCORPBROWSER/1.2 (X11; LINUX; EN-US) BLINK/20420101
userAgent.toAngryString();
类似的,这允许你重写方法。
// AngryUserAgent module
(function AngryUserAgent(UserAgent) {
// Store old .toString() method for later use
var _toString = UserAgent.prototype.toString;
// Overwrite .toString()
UserAgent.prototype.toString = function() {
return _toString.call(this).toUpperCase();
};
})(UserAgent);
var userAgent = new UserAgent;
// ...
// EVILCORPBROWSER/1.2 (X11; LINUX; EN-US) BLINK/20420101
userAgent.toString();
在后面的例子中,允许你的用户获取代码库的命名空间,让你在对扩展和插件的定义方面上的控制变小了。为了让插件遵循一些约定,你可以(或者是应该)写下文档。
##测试
对测试驱动开发 (test-driven development) 来说,写下大纲是良好的开始。简单来说,指的是在你写实际的代码库之前,在你写下测试准则的时候。如果测试检查的是你的代码特性是否跟期待的一样,以及你在写代码库之前写测试,这就是行为驱动开发。不管怎样,如果你的测试覆盖了你的代码库的每一个特性,而且你的代码通过了所有的测试。你可以确定你的代码是可以正常工作的。