Imagine creating a library of wikis. Wiki sites contain content that users can create, delete, and modify interactively. Many wikis feature a simple, text-based markup language for creating content. Typically, these markup languages provide only a subset of the functionality available in HTML, but there is a simpler, clearer source format. For example, text that surrounds the asterisk is formatted as bold, and the underlined text is formatted as an underscore, and the surrounding slash is formatted as italic. Use the notation to enter the following format:
this sentence contains a *bold phrase* within it.this sentence contains a _underlined phrase_ within it.this sentence contains a /italicized phrase/ within it.
The site will present the content to wiki readers in the following form
This sentence contains a bold phrase within it.
This sentence contains a underlined phrase within it.
This sentence contains a italicized phrase within it.
A flexible wiki library should provide an alternative markup language for application writers.
To provide an alternative markup language for application writers, the ability to extract user-created markup source text content is separated from other features. Other wiki features include billing management, revision history, and content storage. The rest of the application can interact with the extracted functionality through an interface with a well-documented set of properties and methods. The application works well regardless of which source format is chosen by rigorous programming of the interface's complete API and ignoring the implementation details of these methods.
Get a deeper understanding of what Wiki content extraction interfaces are needed. The wiki library must be able to extract metadata, such as page headings and author information, and format the page content as HTML for wiki readers. Each page in the wiki can be represented as an object that provides access to the data through the GetTitle, Getauthor, and Tohtml page methods.
Next, the wiki library needs to provide a way to create an application that creates a custom Wiki formatter, as well as some built-in formatters for popular tag formats. For example, the writer of an application might want to use the MediaWiki formatter (which is the format used by Wikipedia).
var app=new Wiki(Wiki.formats.MEDIAWIKI);
The wiki library stores the formatting function inside the Wiki instance object
function Wiki(format){ this.format=format;}
Whenever the reader wants to view the page, the application retrieves its source text and renders the source text as an HTML page using the internal formatter.
Wiki.prototype.displayPage=function(source){ var page=this.format(source); var title=page.getTitle(); var author=page.getAuthor(); var output=page.toHTML(); //...}
How is a formatter like Wiki.formats.MEDIAWIKI implemented? Programmers familiar with class-based programming may prefer to create a page's base class that represents user-created content, and each page's subclass implements a different format. MediaWiki formatting may be implemented as a Mwpage class that inherits the page, while MediaWiki is the factory function that returns the Mwpage instance.
function MWPage(source){ Page.call(this,source); //...}MWPage.prototype=Object.create(Page.prototype);MWPage.prototype.getTitle=function(){};MWPage.prototype.getAuthor=function(){};MWPage.prototype.toHTML=function(){};wiki.formats.MEDIAWIKI=function(source){ return new MWPage(source);}
Because the Mwpage class needs to implement its own gettitle,getauthor,tohtml method for all wiki applications, the page base class does not work. And the Displaypage method does not need to care about the inheritance system of the Page object. It only needs to implement how the page is displayed. The wiki format is free to implement, and any code that can do it can.
It is often sufficient to construct an interface implementation of the MediaWiki page format using simple object literals.
Wiki.formats.MEDIAWIKI=function(source){ //.... return { getTitle:function(){}, getAuthor:function(){}, toHTML:function(){} }};
Inheritance can sometimes lead to more problems than it solves. Inheritance issues arise when several different wiki formats share a feature set that does not overlap. There is no inheritance structure that is right. For example:
Format A:*bold*,[link],/italics/Format B:**bold**,[[link]],*italics*Format A:**bold**,[link],*italics*
We want to implement the functions of each part to identify each different type of input, but the blending and matching of functions does not map to any clear inheritance hierarchy between A, B and C. The correct approach is to implement each type of input matching function separately, and then mix and match the functions according to each format.
Notice that the page base class is eliminated, and there is no need to replace it with anything. Anyone who wants to implement a new custom format can, without having to "register" it somewhere. As long as the Displaypage method is structurally correct and has the expected behavior of the Gettitle,getauthor,tohtml method, then it applies to any JS object.
This interface is sometimes called a struct type or duck type. Any object that has the desired structure will belong to that type (it looks like a duck, or a duck). In JS This is an elegant, lightweight programming pattern, because there is no need to write the displayed declarations. A function that invokes an object method can work with any object that implements the same interface. Of course you list the expected structure of the object interface in the API's documentation. This allows the interface's implementation to know which properties and methods are required and what the library and application expects the behavior to be.
Another benefit of flexible structure types is the benefit of unit testing. The wiki library may expect to embed an HTML server object to implement the network functionality of the wiki site. If you want to test the interaction timing of a wiki site without a network connection, you can implement a mock object to simulate the behavior of the HTTP server. These behaviors follow a predetermined script rather than a real contact network. This approach provides the behavior of repeating interactions with the virtual server, rather than relying on unpredictable network behavior. This is possible using a test component to interact with the server. Tips
Design flexible object interfaces using struct types (also known as duck types)
Structure interfaces are more flexible and lightweight, so you should avoid using inheritance
For unit tests, use mock objects, which are alternative implementations of interfaces, to provide a recheck behavior
[Effective JavaScript Notes] 57th: Designing flexible interfaces using struct types