Composing Software Notes Flashcards
Function composition
the process of applying a function to the output of another function.
given two functions, f and g, (f ◦ g)(x) = f(g(x)). The circle is the composition operator. Called “composed with”. F composed with G
pipe()
creates a pipeline of functions, passing the output of one function to the input of another.
compose() pipes twin
Point-free style
writing functions with the mention of arguments. To do it, call a function that returns the new function, rather than declaring the function explicitly. That means you won’t need the function keyword or the arrow syntax (=>)
Primitives
const firstName = 'Claude'; const lastName = 'Debussy';
A composite
const fullName = {
firstName,
lastName
}
composite datatypes
Arrays, Sets, Map, WeakMaps, TypedArrays, etc
Object Composition
assembling or composing objects to get more complex behavior. Favor object composition over class inheritance (you should form composite objects from small component parts, rather than inheriting all properties from an ancestor in a class hierarchy. Composite objects are formed by putting objects together such that each of the latter is ‘part of’ the former
3 kinds of object compositional relationships
delegation, acquaintance, aggregation
delegation
when an object delegates property access to another object, as used in the state, strategy, and visitor patterns.
acquaintance
when an object knows about another object by reference, usually passed as a parameter; a uses-a relationship e.g a network request handler might be passed a reference to a logger to log the request - the request handler uses a logger
aggregation
when child objects form part of a parent object: a has-a relationship, e.g. DOM children are component elements in a DOM node - a DOM node “has” children
can Class inheritance can be used to construct composite objects
yes but it’s a restrictive and brittle way to do it. You should use a has-a and uses-a relationships over is-a relationships. CI is one kind of composite object construction.
Problems with inheriting all properties from an ancestor in a class hierarchy:
- The tight coupling problem: Because child classes are dependent on the implementation of the parent class, class inheritance is the tightest coupling available in object oriented design.
- The fragile base class problem: Due to tight coupling, changes to the base class can potentially break a large number of descendant classes – potentially in code managed by third parties. The author could break code they’re not aware of.
- The inflexible hierarchy problem: With single ancestor taxonomies, given enough time and evolution, all class taxonomies are eventually wrong for new use-cases.
- The duplication by necessity problem: Due to inflexible hierarchies, new use cases are often implemented by duplication, rather than extension, leading to similar classes which are unexpectedly divergent. Once duplication sets in, it’s not obvious which class new classes should descend from, or why.
- The gorilla/banana problem: “…the problem with object-oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.”
Most common form of object composition in JS
object concatenation. You start with an object and mix in the features you want
Building compsosites with class inheritance:
class Foo { constructor () { this.a = 'a' } } class Bar extends Foo { constructor(options) { super(options); this.b = 'b'
} } const myBar = new Bar(); // {a: 'a', b: 'b'}