“Favor object composition over class inheritance”
the Gang of Four, “Design Patterns: Elements of Reusable Object-Oriented Software”
“...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.”
∼ Joe Armstrong, “Coders at Work”
Overview
Rather than focusing on the OOP capabilities of JS, many folks come to appreciate the relative simplicity and ease of usingcomposition overinheritance.
Put simply:
- Inheritance focuses on establishing 'is a' relationships. Although this practice somewhat dominates the industry, one of the drawbacks is that we must carefully try to plan and anticipate many use cases for 'things' ahead of time. It's not too difficult to overlook something and then have to struggle to backtrack.
- Composition is more about 'has a' relationship - or we focus on what something should be able to do. Essentially, we 'mix in' or compose whatever functionality from wherever we might need it, 'on the fly.' This tends to make us a bit more adaptable, and even the syntax and implementation can be easier to follow.
Thisvideo is a great 'primer' for the concepts expressed here.
Also, as a reference to the 'inheritance version' of the code I go through here:
Composition
Ina previous post, we explored creating newobjects 'on the fly_ usingobject composition. Now we'll take it to the next level.
Function Factories 🏭 to Encapsulate 'Functionality'
Again, in order to build up aStudent orFaculty member, we will want tocompose them from some other objects that represent different pieces of functionality.
constGreeter=(name)=>({greet(){return`👋🏽. My name is,${name}.`;},});constCourseAdder=()=>({addCourse(newCourse){this.currentCourses=[newCourse,...this.currentCourses];},});constRaiseEarner=()=>({giveRaise(raiseAmt){this.salary+=raiseAmt;},});Done! We have 3 'things' that we can compose together to build 'bigger' things. Again, we capitalize to denote that these are 'things' made to be reused or composed (same convention with React components).
Each of these is afunction factory that returns anobject. that 'wraps' 🎁 amethod to implement some specific functionalities.
constGreeter=(name)=>({greet(){return`👋🏽. My name is,${name}.`;Greeter encapsulatesname in aclosure. It means that afterGreeter isinvoked, instead ofname beinggarbage collected,name will stick around as long as it's needed bygreet.
We'll see 👇🏽 thatGreeter will becomposed intoStudent andFaculty. Whenever we create aStudent, for example, thename that we use will be 'enclosed' ingreet. Each timegreet is run 🏃🏽♂️ on aStudent, that 'enclosed'name will bereferenced.
Student andFaculty
constStudent=({id,name,age,major,credits,gpa,currentCourses})=>({id,name,age,major,credits,gpa,currentCourses,...Greeter(name),...CourseAdder(),});constFaculty=({id,name,age,tenured,salary})=>({id,name,age,tenured,salary,...Greeter(name),...RaiseEarner(),});BothFaculty andGreeter 'mix in' 👩🏽🍳 the desired functionalities. For example:...Greeter(name),. We can say that each of thesehas aGreeter.
When we mix inGreeter, we arebinding thename - there's thatclosure.
RaiseEarner andCourseAdder areinvoked andbound with athis-this.currentCourses andthis.salary.
Next, we'll instantiatemark, an instance ofStudent andrichard, an instance ofFaculty.
constmark=Student({id:1124289,name:"Mark Galloway",age:53,major:"Undeclared",credits:{current:12,cumulative:20,},gpa:{current:3.4,cumulative:3.66,},currentCourses:["Calc I","Chemistry","American History"],});constrichard=Faculty({id:224567,name:"Richard Fleir",age:72,tenured:true,salary:77552,});All we do is pass in their unique properties, ourfunction factories crank them out.
Testing things out:
mark.addCourse("Psych");richard.giveRaise(5000);console.log(mark.greet(),richard.greet());console.log(mark,richard);We can see:
👋🏽. My name is, Mark Galloway. 👋🏽. My name is, Richard Fleir.{id: 1124289, name:'Mark Galloway', age: 53, major:'Undeclared', credits:{ current: 12, cumulative: 20}, gpa:{ current: 3.4, cumulative: 3.66}, currentCourses:['Psych','Calc I','Chemistry','American History'], greet:[Function: greet], addCourse:[Function: addCourse]}{id: 224567, name:'Richard Fleir', age: 72, tenured:true, salary: 82552, greet:[Function: greet], giveRaise:[Function: giveRaise]}The Main Advantage Over Inheritance
I prefer this implementation better, both syntactically and conceptually. I like to think of what things 'do' rather than what they 'are.'
But...here's a 'real' advantage. What if, we have aStudent...that gets hired on to teach also? So...aFacultyStudent - one that can add courses and/or might get a raise.
A possibly unpredicted situation such as this really shows wherecomposition shines ✨.
More Inheritance? 👎🏽
Maybe. But...what is aFacultyStudent...should it extend fromStudent orFaculty? In classical OOP, this is where you might create aninterface - a means to encapsulate functionality that can be implemented by variousclasses. Fine.
More Composition 👍🏽
How about this instead?
constFacultyStudent=({id,name,age,major,credits,gpa,currentCourses,tenured,salary,})=>({id,name,age,major,credits,gpa,currentCourses,tenured,salary,// Composition!...Greeter(name),...CourseAdder(),...RaiseEarner(),});constlawrence=FacultyStudent({id:1124399,name:"Lawrence Pearbaum",age:55,major:"CIS",credits:{current:12,cumulative:0,},gpa:{current:0.0,cumulative:0.0,},currentCourses:["JavaScript I"],tenured:false,salary:48000,});lawrence.addCourse("JavaScript II");lawrence.giveRaise(2000);console.log(lawrence.greet(),lawrence);👋🏽. My name is, Lawrence Pearbaum.{id: 1124399, name:'Lawrence Pearbaum', age: 55, major:'CIS', credits:{ current: 12, cumulative: 0}, gpa:{ current: 0, cumulative: 0}, currentCourses:['JavaScript II','JavaScript I'], tenured:false, salary: 50000, greet:[Function: greet], addCourse:[Function: addCourse], giveRaise:[Function: giveRaise]}Essentially, we can just keep applying the same concepts over and over andcompose 'on the fly'... "all night long!" 🎵
All together now, and IK that was a lot! Thanks for sticking around.
Updated 1: If this topic is of interest to you, I refer you to this excellent article that's a few years old but still presents some of the same information in a slightly different way:
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse






