面向对象的Javascript之三(封装和信息隐藏)(3)


var Publication = new Interface("Publication", ["getIsbn", "setIsbn", "getTitle", "setTitle", "getAuthor", "setAuthor", "display"]);
var Book = (function() {
// private static attribute
var numsOfBooks = 0;
// private static method
function checkIsbn(isbn) {
if(isbn == undefined || typeof isbn != "string") return false;
isbn = isbn.replace("-", "");
if(isbn.length != 10 && isbn.length != 13) return false;
var sum = 0;
if(isbn.length == 10) {
if(!isbn.match(\^\d{9}\)) return false;
for(var i = 0;i < 9;i++) {
sum += isbn.charAt(i) * (10 - i);
}
var checksum = sum % 11;
if(checksum == 10) checksum = "X";
if(isbn.charAt(9) != checksum) return false;
} else {
if(!isbn.match(\^\d{12}\)) return false;
for(var i = 0;i < 12;i++) {
sum += isbn.charAt(i) * (i % 2 == 0 ? 1 : 3);
}
var checksum = sum % 10;
if(isbn.charAt(12) != checksum) return false;
}
return true;
}
// return constructor
return function(newIsbn, newTitle, newAuthor) {
// private attribute
var isbn, title, author;
// previleged method
this.getIsbn = function() {
return isbn;
};
this.setIsbn = function(newIsbn) {
if(!Book.checkIsbn(newIsbn)) {
throw new Error("Book: Invalid ISBN.");
}
isbn = newIsbn;
}
this.getTitle = function() {
return title;
},
this.setTitle = function(newTitle) {
title = newTitle || "";
},
this.getAuthor = function() {
return author;
},
this.setAuthor = function(newAuthor) {
author = newAuthor || "";
}
Book.numsOfBooks++;
if(Book.numsOfBooks > 50) {
throw new Error("Book: at most 50 instances of Book can be created.");
}
// implements Publication interface
this.setIsbn(newIsbn);
this.setTitle(newTitle);
this.setAuthor(newAuthor);
};
})();
// public static methods
Book.convertToTitle = function(title) {
return title.toUpperCase();
}
// public methods
Book.prototype = {
display: function() {
return "Book: ISBN: " + this.getIsbn() + ",Title: " + this.getTitle() + ",Author: " + this.getAuthor();
}
};


这种方案与上种相似,使用var和this来创建私有成员和特权方法。不同之处在于使用闭包来返回构造器,并将checkIsbn声明为私有静态方法。可能有人会问,我为什么要创建私有静态方法,答案在于使所有对象公用一份函数副本而已。我们这里创建的50个实例都只有一个方法副本checkIsbn,且属于类Book。根据需要,你也可以创建公有的静态方法供外部调用(如:convertToTitle)。这里我们继续考虑一个问题,假设以后我们需要对不同的书做限制,比如<<Javascript高级编程>>最大印发量为500,<<.NET>>最大印发量为1000,也即说需要一个最大印发量的常量。思考一下,利用已有的知识,我们如何声明一个常量呢?其实不难,我们想想,可以利用一个只有访问器的私有特权方法就可以实现。

复制代码 代码如下:


var Publication = new Interface("Publication", ["getIsbn", "setIsbn", "getTitle", "setTitle", "getAuthor", "setAuthor", "display"]);
var Book = (function() {
// private static attribute
var numsOfBooks = 0;
// private static contant
var Constants = {
"MAX_JAVASCRIPT_NUMS": 500,
"MAX_NET_NUMS": 1000
};
// private static previleged method
this.getMaxNums(name) {
return Constants[name.ToUpperCase()];
}
// private static method
function checkIsbn(isbn) {
if(isbn == undefined || typeof isbn != "string") return false;
isbn = isbn.replace("-", "");
if(isbn.length != 10 && isbn.length != 13) return false;
var sum = 0;
if(isbn.length == 10) {
if(!isbn.match(\^\d{9}\)) return false;
for(var i = 0;i < 9;i++) {
sum += isbn.charAt(i) * (10 - i);
}
var checksum = sum % 11;
if(checksum == 10) checksum = "X";
if(isbn.charAt(9) != checksum) return false;
} else {
if(!isbn.match(\^\d{12}\)) return false;
for(var i = 0;i < 12;i++) {
sum += isbn.charAt(i) * (i % 2 == 0 ? 1 : 3);
}
var checksum = sum % 10;
if(isbn.charAt(12) != checksum) return false;
}
return true;
}
// return constructor
return function(newIsbn, newTitle, newAuthor) {
// private attribute
var isbn, title, author;
// previleged method
this.getIsbn = function() {
return isbn;
};
this.setIsbn = function(newIsbn) {
if(!Book.checkIsbn(newIsbn)) {
throw new Error("Book: Invalid ISBN.");
}
isbn = newIsbn;
}
this.getTitle = function() {
return title;
},
this.setTitle = function(newTitle) {
title = newTitle || "";
},
this.getAuthor = function() {
return author;
},
this.setAuthor = function(newAuthor) {
author = newAuthor || "";
}
Book.numsOfBooks++;
if(Book.numsOfBooks > 50) {
throw new Error("Book: at most 50 instances of Book can be created.");
}
// implements Publication interface
this.setIsbn(newIsbn);
this.setTitle(newTitle);
this.setAuthor(newAuthor);
};
})();
// public static methods
Book.convertToTitle = function(title) {
return title.toUpperCase();
}
// public methods
Book.prototype = {
display: function() {
return "Book: ISBN: " + this.getIsbn() + ",Title: " + this.getTitle() +
",Author: " + this.getAuthor() + ", Maximum: ";
},
showMaxNums: function() {
return Book.getMaxNums("MAX_JAVASCRIPT_NUMS");
}
};


最完美的情况就是你所封装的程序对调用者而言,仅仅需要知道你的接口就可以,根本不关心你如何实现。但问题在于,随着工程量的扩大,你的封装内容必然会增大,在项目发生交接时,对于一个对作用域和闭包等概念不熟悉的成员来说,维护难度会变得如此之大。有些时候应需求响应必须改动源码(这里不一定指改接口),可能是新增一些细节,即使拿到你的源码却无从下手,那就不好做了。因此,我的建议:封装不要过度,接口一定要清晰,可扩展。

您可能感兴趣的文章:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wdjddw.html