Home  >  Article  >  Web Front-end  >  Object-oriented Javascript Part 2 (Introduction to interface implementation)_js object-oriented

Object-oriented Javascript Part 2 (Introduction to interface implementation)_js object-oriented

WBOY
WBOYOriginal
2016-05-16 17:56:43903browse

It is enough to show how important interfaces are in the object-oriented field. But JS does not have a built-in interface mechanism like other high-level object-oriented languages ​​(C#, Java, C, etc.) to determine that one set of objects contains similar characteristics to another set of objects. Fortunately, JS has great flexibility (which I talked about above), which makes it very simple to imitate interface features. So what exactly is an interface?

Interface provides unified method definitions between some classes with similar behavior (may be of the same type or different types), so that these classes can communicate well.

What are the benefits of using interfaces? Simply put, it can improve the reusability of similar modules in the system and make different types of communications more robust. Once an interface is implemented, all methods in the interface must be implemented. For large-scale Web projects, multiple complex modules with similar functional modules only need to provide an interface to provide an implementation without affecting each other. But we must be clear that interfaces are not omnipotent. Since JS is a weakly typed language, you cannot force other team members to strictly follow the interfaces you provide, but you can use code specifications and auxiliary classes to alleviate this problem. In addition, it will also have a certain impact on system performance, which should be determined according to the complexity of your system requirements. Since there are no built-in interface and implements keywords, let's take a look at how JS imitates and implements interfaces.

1. The simplest and least effective way to implement an interface is to use annotations. That is, use interface in comments to explain the intent of the interface.

Copy code The code is as follows:

/*
interface Composite {
function add(child);
function remove(child);
function getChild(index);
}
interface FormItem {
funtion save();
}
*/
var CompositeForm = function(id, name, action) {
// implements Composite, FormItem
}
CompositeForm.prototype = {
// implements Composite interface
add: function (child) {
//...
},
remove: function(child) {
//...
},
getChild: function(index) {
//...
}
// implements FormItem interface
save: function() {
//...
}
}

This does not do a good job of simulating the functionality of the interface and ensuring that the Composite class actually implements the set of methods. It does not throw an error to notify the programmer of the problem. It has no effect other than explanation. All consistency is required. Programmers do it voluntarily. But it is easy to implement, does not require additional classes or functions, does not affect the size and execution speed of the document, and comments can be easily stripped, which improves reusability to a certain extent, because the description of the class provided can be the same as other implementations Interface classes communicate.

2. Check the mock interface with properties. The class explicitly declares the interface to be implemented, and checks whether the corresponding interface is implemented through attributes.
Copy code The code is as follows:

/*
interface Composite {
function add(child);
function remove(child);
function getChild(index);
}
interface FormItem {
funtion save();
}
*/
var CompositeForm = function(id, name, action) {
this.implementsInterfaces = ["Composite", "FormItem"];
//...
}
function checkInterfaces( formInstance) {
if(!implements(formInstance, "Composite", "FormItem")) {
throw new Error("Object doesn't implement required interface.");
}
/ /...
}
//check to see if an instance object declares that it implements the required interface
function implements(instance) {
for(var i = 1; i < arguments .length; i ) {
var interfaceName = arguments[i];
var isFound = false;
for(var j = 0; j < instance.implementsInterfaces.length; j ) {
if(interfaceName == instance.implementsInterfaces[j]) {
isFound = true;
break;
}
}
if(!isFound) return false;// An interface was not found.
}
return true;// All interfaces were found.
}

Found here, comments still added to illustrate the interface. However, an attribute implementsInterfaces is added to the Composite class to indicate which interfaces the class must implement. Check this property to determine whether the corresponding interface is implemented. If it is not implemented, an error will be thrown. But the disadvantage is that you still can't judge whether the corresponding interface method is actually implemented. You just "claim" to have implemented the interface, which also increases the corresponding workload.

3. Use "duck pattern recognition" to implement the interface. It does not matter whether a class supports the implemented interface from the property inspection implementation. As long as all the methods in the interface appear in the corresponding places in the class, it is enough to indicate that the interface has been implemented. Just like "If it walks like a duck and quacks like a duck, no matter whether it has a label saying it is a duck or not, then we think it is a duck." Use auxiliary classes to determine whether a class exists (implements) all the methods in the corresponding interface. If it does not exist, it means that it is not implemented.
Copy code The code is as follows:

// Interfaces
var Composite = new Interface( "Composite", ["add", "remove", "getChild"]);
var FormItem = new Interface("FormItem", ["save"]);

var CompositeForm = function( id, name, action) {
// implements Composite, FormItem interfaces
}
function checkInterfaces(formInstance) {
Interface.ensureImplements(formInstance, "Composite", "FormItem");
//...
}

Interface class
Copy code The code is as follows:

// Interface class is for checking if an instance object implements all methods of required interface
var Interface = function(name, methods) {
if(arguments.length != 2 ) {
throw new Error("Interface constructor expects 2 arguments, but exactly provided for " arguments.length " arguments.");
}
this.name = name;
this.methods = [];
for(var i = 0;i < methods.length; i ) {
if(typeof methods[i] != "string") {
throw new Error("Interface constructor expects to pass a string method name.");
}
this.methods.push(methods[i]);
}
}
//static class method
Interface .ensureImplements = function(instance) {
if(arguments.length < 2) {
throw new Error("Function Interface.ensureImplements expects at least 2 arguments, but exactly passed for " arguments.length " arguments. ");
}
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 at least 2 arguments to be instances of Interface.");
}
for(var j = 0, mLen = interface.methods .length; j < mLen; j ) {
var method = interface.methods[j];
if(!instance[method] || typeof instance[method] != "function") {
throw new Error("Function Interface.ensureImplements: object doesn't implements " interface.name ". Method " method " wasn't found.");
}
}
}
}

Strict type checking is not always necessary, and the above interface mechanism is rarely used in normal web front-end development. But when you face a complex system, especially one with many similar modules, interface-oriented programming will become very important. It seems to reduce the flexibility of JS, but in fact it improves the flexibility of classes and reduces the coupling between classes, because when you pass in any object that implements the same interface, it can be parsed correctly. So when is it appropriate to use interfaces? For a large-scale project, there must be many team members, and the project will be split into more fine-grained functional modules. In order to ensure the progress, a "placeholder program" (interface) must be used in advance to explain the function of the module or to be related to the developed module. When communicating between completed modules, it is necessary to provide a unified interface (API). As the project continues to advance, the requirements may continue to change, and the functions of each module will also change accordingly. However, the communication between each other and the API provided to the upper modules will always remain unchanged, ensuring the stability and durability of the entire architecture. sex. Below we use a specific example to illustrate the practical application of the interface. Assume that a class is designed to automatically detect the result object (TestResult class) and format it to output a web page view without using the interface implementation:
Copy code The code is as follows:

var ResultFormatter = function(resultObject) {
if(!(resultObject instanceof TestResult)) {
throw new Error("ResultFormatter 생성자는 TestResult의 인스턴스를 예상합니다.");
this.resultObject = resultObject;
}
ResultFormatter.prototype.render = function() {
var date = this.resultObject.getDate()
var items = this.resultObject. getResults();
var 컨테이너 = document.createElement("div");
var header = document.createElement("h3")

header.innerHTML = "" 날짜 .toUTCString();
container.appendChild(header);
var list = document.createElement("ul");
container.appendChild(list)

for(var i = 0, len = items.length; i ) {
var item = document.createElement("li")
item.innerHTML = items[i]
list.appendChild(item); 🎜>}
return 컨테이너;
}


우선 ResultFormatter 클래스의 생성자는 TestResult 인스턴스인지 여부만 확인하지만 getDate 메소드가 해당 인스턴스인지 여부는 보장하지 않습니다. ()가 렌더링되고 getResults()가 구현됩니다. 또한 요구 사항이 계속 변경됨에 따라 이제 getDate() 및 getResults() 메서드를 포함하는 Weather 클래스가 있지만 TestResult의 인스턴스인지 여부만 확인할 수 있기 때문에 render 메서드를 실행할 수 없습니다. 말이 너무 없네? 해결책은 수표 인스턴스를 제거하고 인터페이스로 교체하는 것입니다.


//ResultSet 인터페이스 생성
var ResultSet = new Interface("ResultSet", ["getDate", "getResults"]);
var ResultFormatter = function(resultObject) {
// Interface.ensureImplements를 사용하여 resultObject 확인
인터페이스. verifyImplements(resultObject , ResultSet);
this.resultObject = resultObject;
}
ResultFormatter.prototype.render = function() {
// 이전과 동일하게 유지
var date = this .resultObject.getDate();
var items = this.resultObject.getResults();
var 컨테이너 = document.createElement("div")
var header = document.createElement("h3") ;

header.innerHTML = "date.toUTCString()"의 테스트 결과
container.appendChild(header)
var list = document.createElement("ul"); 컨테이너.appendChild(목록);

for(var i = 0, len = items.length; i ) {
var item = document.createElement("li")
item.innerHTML = items[ i];
list.appendChild(item);
}
return 컨테이너
}


렌더링 방법이 변경되지 않은 것을 볼 수 있습니다. 어떤 식으로든. 변경된 것은 인터페이스를 추가하고 유형 확인을 위해 인터페이스를 사용하는 것뿐이었습니다. 동시에 이제 Weather 클래스의 인스턴스를 전달하여 호출할 수 있습니다. 물론 ResultSet 인터페이스를 구현하는 모든 클래스의 인스턴스를 전달하여 검사를 더욱 정확하고 관대하게 만들 수도 있습니다. 이후 JS 디자인 패턴이 도입되면서 인터페이스는 팩토리 모드, 조합 모드, 데코레이션 모드 및 명령 모드에서 널리 사용됩니다. 인터페이스가 JS 모듈식 디자인에 가져오는 이점을 모두가 즐길 수 있기를 바랍니다.
Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn