Mixins

Objective: Create methods that can be injected into classes of our choice. Methods should have access to the fields of the instances. Injection should not involve inheritance.

Python

Python supports multiple-inheritance. It is natural to use it for this use case:

class Greeting:
    def hello(self):
        print(f"Hello, my name is {self.name}. How can I help you?")

class Farewell:
    def bye(self):
        print("Thank you. Bye!")

class ChatBot(Greeting, Farewell):
    def __init__(self, name):
        self.name = name

bot = ChatBot("R2D2")
bot.hello()
bot.bye()

Using inheritance hovewer violates definition of a mixin. If this is important for the design, we can copy methods without affecting MRO:

def mixin(*sources):
    def decorator(target):
        for src in sources:
            for key in dir(src):
                if not (key.startswith('__') and key.endswith('__')):
                    setattr(target, key, getattr(src, key))
        return target
    return decorator

class Greeting:
    def hello(self):
        print(f"Hello, my name is {self.name}. How can I help you?")

class Farewell:
    def bye(self):
        print("Thank you. Bye!")

@mixin(Greeting, Farewell)
class ChatBot:
    def __init__(self, name):
        self.name = name

bot = ChatBot("R2D2")
bot.hello()
bot.bye()

More sophisticated packages like mixin can be used.

Rust

Rust doesn't support inheritance. Traits are natural way of fulfilling objectives.

Note that associated functions can't directly access fields of the structure. For this reason we define helper trait Named.

trait Named {
    fn name(&self) -> &str;
}

trait Greeting : Named {
    fn hello(&self) {
        println!("Hello, my name is {}. How can I help you?", self.name());
    }
}

trait Farewell {
    fn bye(&self) {
        println!("Thank you. Bye!");
    }
}

struct ChatBot {
    name : String
}

impl Greeting for ChatBot {}
impl Farewell for ChatBot {}
impl Named for ChatBot {
    fn name(&self) -> &str {
        &self.name
    }
}

fn main() {
  let bot = ChatBot{name: "R2D2".to_string()};
  bot.hello();
  bot.bye();
}

Crystal

Crystal supports single-inheritance. Mixins can be implemented using modules. Methods in the module can directly access fields of the structure. This is checked at compile time.

module Greeting
  def hello
    puts "Hello, my name is #{@name}. How can I help you?"
  end
end

module Farewell
  def bye
    puts "Thank you. Bye!"
  end
end

class ChatBot
  include Greeting
  include Farewell

  def initialize(@name : String)
  end
end

trx = ChatBot.new "R2D2"
trx.hello
trx.bye