Share via


TypeScript: Implementing Mixin Pattern

Overview

Mixin is an object-oriented programming concept that has been used successfully in enterprise application development scenarios. In an object-oriented programming language, Mixin is a class which contains methods from other classes. Mixin class does not necessarily have to be the parent class of all those other classes. That means, Mixin can be referred to as “included” instead of “inherited”.

Mixin avoids DoD (Diamond of Death)

Mixin brings in the code reusability and can be used to avoid the classic diamond of death problem because of inheritance ambiguity that can be caused by multiple inheritance.

What is Diamond of Death?

Diamond of death refers to the ambiguity that can occur when two classes (example parent1 and parent2) inherit from one base class (base) and another class (Child) inherits from classes parent1 and parent2. 

Suppose, base class contains a method named Display(). Classes paren1 and parent2 overrides the Display() method. However, Child class does not override Display() method. So, when an instance of class Child calls Display() method, which version of the method will be called? parent1 or parent2?

This is referred to as diamond of death issues because of the shape of the inheritance diagram (diamond).

How does Mixin avoid DoD?

Mixin class does not involve any inheritance and can contain methods from other classes. This helps to avoid the diamond of death issue. Although virtual inheritance can address DoD, the alternatives of Mixin can be used effectively. Mixin pattern is very popular in Jthe avaScript community.

Mixin helps to implement Dependency inversion principle

Dependency inversion principle (DIP) is D in SOLID principles and refers to the decoupling of modules.

  1. High-level modules should not directly depend on low-level modules; instead, both should depend upon abstraction.
  2. Abstraction should not depend upon details; instead, details should depend upon abstraction.

Mixin helps us to obey the dependency inversion principle by providing abstraction and helps to inject the dependencies.

Mixin in action

Mixin allows defining a set of functionalities to an object. Mixin class defines distinct functionality. Other classes can then include Mixin class and access its properties and methods.

The Mixin Function

In a simple scenario, class B can extend class A to get its functionality through inheritance. But Instead of class A extending class B, function B can take class A and return new class with the required added functionality. Here, function B is a Mixin function.

A Mixin function,

  1. Takes constructor
  2. Creates a class that extends the constructor
  3. Adds new functionality to the new class
  4. Returns new class
Note

The code sample is written using TypeScript for demonstration purposes.

The simplest form of Mixin function is as follows,

type Constructor<T = {}> = new  (...args: any[]) => T;  
   
// A mixin that adds a property  
function Timestamped<TBase extends Constructor>(Base: TBase) {  
  return class  extends Base {  
    timestamp = Date.now();  
  };  
}  
   
// a mixin that adds a property and methods  
function Activatable<TBase extends Constructor>(Base: TBase) {  
  return class  extends Base {  
    isActivated = false;  
   
    activate() {  
      this.isActivated = true;  
    }  
   
    deactivate() {  
      this.isActivated = false;  
    }  
  };  
}

We will take a simple example to demonstrate Mixin

Let’s go into the details of each class.

// Base class  
 class Person {  
       constructor(public name:string, public  age:number){  
       }  
    
       showInfo() {  
             alert(“Name: “ + this.name + “ Age: “ + this.age);  
       }  
 }  
    
 // First Mixin (A class can implement multiple Mixin)  
 class Programmer {  
       Code() {  
             alert(“Programmer.Code”);  
       }  
 }  
    
 // Second Mixin (A class can implement multiple Mixin)  
 class Manager {  
       Manage() {  
             alert(“Manager.Manage”);  
       }  
 }  
Now, let us have a library code for  Mixin
 
 
 function applyMixins(derivedCtor: any, baseCtors: any[]) {  
     baseCtors.forEach(baseCtor => {  
        Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {  
              if (name !== 'constructor') {  
                 derivedCtor.prototype[name] = baseCtor.prototype[name];  
             }  
        });  
     });  
 }  

This function should be included in a program such that it can be accessed by all Mixins.

Now, let’s implement the actual Employee class using available Mixin by treating them like an interface.

// Child class that inherits/extends from base class and implements multiple classes an Mixins  
 class Employee extends Person implements Programmer, Manager {  
       constructor(name, age, public  salary: number) {  
             super(name, age); // call base class constructor  
       }  
    
       showInfo() {  
             alert(“Name: “ + this.name + “ Age: “ + this.age + “ Salary: “ + this.salary);  
       }  
    
       Code: () => void 
       Manage: () => void 
 }

applyMixins(Employee, [Programmer, Manager]);  

A few things to consider here,

  • Use the implements keyword, and not extends
  • Have matching signature to compile the code. Implementation will be provided by Mixin
  • Call applyMixins with correct arguments

Let’s test out our Mixin by writing simple test code,

var per: Person = new  Person(‘Nanddeep’, 30);  
per.showInfo(); // calls showInfo of Person class   
    
 var emp = new  Employee(‘Sachin’, 50, 300);  
 emp. showInfo(); // calls showInfo of Employee class   
   
emp.Code(); // calls Programmer.Code  
emp.Manage(); // calls Manager.Manage

Summary

Mixin pattern is useful in object oriented programming to write the code with best practices and follow the SOLID principles.