Скачать книгу

down to how it obtains the x and y coordinates for each data point.

      By the way, one of the casualties of our abridgement of D3’s code was the interpolate function. In the full version, D3 lets you specify this just as you can specify the functions that obtain x and y. And what does interpolate do? It connects the points in an SVG path. The default is to connect the points with straight line segments, but you could plug in an interpolator that constructs graceful curves instead, and D3 supplies several such interpolators.

      Thus, when it comes right down to it, rj3.svg.line really doesn’t “know” much. All it does is return a function (the inner line) that can create an SVG path out of an array of data points – somehow.

      What reasons could there be for rj3.svg.line to change? Its one responsibility is to produce an SVG path from an array. Everything about how it carries out that responsibility is external to the function and therefore not a reason for it to change!

      All together now: “Mike Bostock, you make it look so easy!”

      The Open/Closed Principle

      This principle states that “Software entities should be open for extension, but closed for modification” (http://www.objectmentor.com/resources/articles/ocp.pdf).

      In other words, you should never change working code. Instead, reuse it somehow (for example, by inheritance) and extend it.

      This, too, is a tall order. Robert Martin even admits in the same article, “In general, no matter how ‘closed’ a module is, there will always be some kind of change against which it is not closed. Since closure cannot be complete, it must be strategic. That is, the designer must choose the kinds of changes against which to close his design. This takes a certain amount of prescience derived from experience.”

      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):

      Конец ознакомительного фрагмента.

      Текст предоставлен ООО «ЛитРес».

      Прочитайте эту книгу целиком, купив полную легальную версию на ЛитРес.

      Безопасно

Скачать книгу