Reliable JavaScript. Lawrence Spencer
Чтение книги онлайн.
Читать онлайн книгу Reliable JavaScript - Lawrence Spencer страница 10
When Mike Bostock designed his d3.svg.line
function, he anticipated changes in the way coordinates might be plucked from data and how the points might be joined (interpolated), and he wisely abstracted those features out of his function.
What he did not think would change (at least not in a backward-incompatible way) was the SVG path specification. He dared to hard-code that a path could always start with "M"
and continue with the points in order, as a text string.
Short of a breaking change to the SVG spec, it is hard to imagine how d3.svg.line
would ever have to change.
The Liskov Substitution Principle
“The what??” you ask!
Coined by Barbara Liskov in a formal way in Data Abstraction and Hierarchy (SIGPLAN Notices 23, 5 [May, 1988]), this principle might be stated more colloquially for a JavaScript context as follows:
Code written to use an object of a certain type should not have to change if provided with an object of a derived type.
Another way of saying this is that when you derive one object from another, the base-level semantics should not change.
If you find yourself writing branching logic so that your function does one thing if provided with a base class, but something else for a derived class, you have violated this principle.
This does not apply to types that do not derive from each other. For example, it is a common and good practice in JavaScript for a function to branch one way if an argument is a Number, another way if it’s a String, and a third way if it’s not there at all and therefore of the Undefined type. As discussed previously, that’s how JavaScript fulfills the object-oriented idea of function overloading.
Incidentally, the use of duck-typing, while not the same as derivation, is very much in the spirit of this principle!
The Interface Segregation Principle
This principle arose in a milieu of interface-based languages such as C++ and Java. In those languages, an interface is a piece of code that describes the functions in a class (names, parameters, and return types) without implementing those functions.
The idea is that an interface with many functions should be broken up into smaller, cohesive parts. Consumers should rely on only one of the mini-interfaces, not on the “fat” whole.
Of course, this is in the service of minimizing the width of the connections between modules. As stated previously, trimming the channels of communication is critical to making large JavaScript systems manageable.
But wait a minute! In JavaScript, there are neither classes nor interfaces. Does that mean JavaScript programmers cannot experience the benefits of following this principle?
Not at all. In fact, we will devote all of Chapter 16 to how to implement this principle in JavaScript. In the meantime, here’s a preview: To follow the spirit of the Interface Segregation Principle, a function can make clear what it expects of its arguments, and those expectations should be minimized. As stated earlier, duck typing is your friend here. Rather than expecting an argument of a certain type, just expect it to have the few properties of that type that you actually need. The ContractRegistry
that will be developed in Chapters 16 through 21 provides a formal way to make the expectations clear and to enforce them. If the ContractRegistry
is not to your taste, you can always write argument-validation code or even write comments!
The Dependency Inversion Principle
This principle, too, was developed with interfaces in mind. Robert Martin states it thus: “High-level modules should not depend upon low-level modules. Both should depend upon abstractions” (http://www.objectmentor.com/resources/articles/dip.pdf).
In an interface-based language, this principle usually finds its expression in the related idea of dependency injection. If class A needs the services of B, it does not construct B. Instead, one parameter to A’s constructor is an interface that describes B. A no longer depends on B, but on its interface. When A is constructed, a concrete B is passed in. B, too, depends on its interface.
The benefit is that a derived version of B, which also fulfills the interface, can be supplied instead thanks to the Liskov Substitution Principle. Furthermore, if B does need to change (in spite of the Open/Closed Principle), the interface concisely describes how it must continue to behave in order to be backward-compatible.
Once again, in JavaScript there are no abstractions, but JavaScript programmers can still program in the spirit of this principle and enjoy its benefits.
The full version of D3’s d3.svg.line
function starts like this (from https://github.com/mbostock/d3/blob/master/src/svg/line.js):
The projection parameter of d3_svg_line
is used to possibly project the data points to another coordinate space. By default, projection is d3_identity
, which makes no changes to the points at all. However, other projections are possible. For example, d3.svg.line.radial
uses polar coordinates (an angle and distance from the origin) by injecting the d3_svg_lineRadial
projection (https://github.com/mbostock/d3/blob/master/src/svg/line-radial.js):
Конец ознакомительного фрагмента.
Текст предоставлен ООО «ЛитРес».
Прочитайте эту книгу целиком, купив полную легальную версию на ЛитРес.
Безопасно оплатить книгу можно банковской картой Visa, MasterCard, Maestro, со счета мобильного телефона, с платежного терминала, в салоне МТС или Связной, через PayPal, WebMoney, Яндекс.Деньги, QIWI Кошелек, бонусными картами или другим удобным Вам способом.