Search code examples
c#interfaceabstract-classmultiple-inheritance

C# 7.0 - Achieve multiple inheritance with default implementation


The question is: how to provide both multiple inheritance (not allowed by C#) and default methods implementation (not allowed by interface in C# < 8.0)?

The general scenario is when I have a first abstraction layer, then a second abstraction layer generating two distinct families/categories, finally a concrete implementation of something that belongs to both the above families/categories.

The example code I wrote is the following (but please do not focus too much on the implementation details, just consider this as a descriptive scenario):

public abstract class Message
{
    protected Guid _id;
    protected string _body;
    protected Message(string body)
    {
        _body = body;
    }
    public override string ToString()
    {
        return _id + "|" + _body;
    }
}
public abstract class ReceivableMessage : Message
{
    private string _receivedFrom;
    public ReceivableMessage(string body, string receivedFrom)
    : base(body)
    {
        _receivedFrom = receivedFrom;
    }
    public string GenerateReply()
    {
        //do some generation
        return "";
    }
}
public abstract class SendableMessage : Message
{
    private string _sendTo;
    public SendableMessage(string body, string sendTo)
    : base(body)
    {
        _sendTo = sendTo;
    }
    public bool CheckReply(ReceivableMessage reply)
    {
        //do some check
        return true;
    }
}

then I have several concrete implementations of both ReceivableMessage and SendableMessage; but now I want to add concrete classes AckMessage and NackMessage...they should extend both ReceivableMessage and SendableMessage (because both can be received and can be sent)...how can I achieve this?
As additional comments I have considered two possibilities but discarded them:

  1. substitute 3 abstract classes with 3 interfaces. Not ok, because I would lose the common method implementations ToString, GenerateReply and CheckReply and I should repeat them in all the concrete classes, as well as the common fields _id, _body, _receivedFrom and _sendTo (even if technically this could be avoided replacing them with properties).
  2. provide concrete implementations for AckReceivableMessage and NackReceivableMessage (inheriting from ReceivableMessage), as well as AckSendableMessage and NackSendableMessage (inheriting from SendableMessage). Not ok, this seems to me code duplication.

Solution

  • I created multiple inheritance by using dynamic dispatch. It's an overly complicated way to accomplish the properties of a mass customization system. What you fundamentally want is the ability to integrate processing from multiple modules. Manufacturing architecture allows a container to infinitely grow -- these terms are from business -- in two ways:

    • Vertically: extending the process (instead of subclassing to do it)
    • Horizontally: adding new processes (instead of subclassing or inheriting to do it)

    Multiple inheritance's other problem is that it introduces ambiguity. It is unclear which method should be invoked when both classes are inherited.

        class A
        {
            public void One() { ... }
        }
        class B
        {
            public void One() { ... }
        }
    

    Manufacturing architecture such as mass customization, however, models processes as whole classes not just methods, and so the above problem is prevented by namespaces.

    namespace A
    {
        class OneProduct { ... }
        class One : Producer<OneProduct>, IProcess { ... }
    }
    
    namespace B
    {
        class OneProduct { ... }
        class One : Producer<OneProduct>, IProcess { ... }
    }
    
    // example of a hardcoded process
    namespace IntegratingProcess
    {
        class MyProduct { ... }
        class MyProcess : Producer<OneProduct>, IProcess 
        {
            private A.One Machine1 { get; set; }
            private B.One Machine2 { get; set; }
            void D() { // allocate memory for all machines and product DTO }
            void O() { // binds Machine1 and Machine2 to MyProduct reference properties }
            void M()
            {
                Machine1.M();
                Machine2.M();
            }
        }
    
    }
    

    Mass customization allows you to dynamically integrate processing and change firing order. This is of course an alternative to the hardcoded form above, where the production process and its firing order is built at compile-time. My R article covers mass customization, but I don't give away the source code for it.

    I am the developer of POWER, a manufacturing architecture and the rules to effectively wield it. My articles instruct programmers on how to correctly model collaborative work with source code.

    http://www.powersemantics.com/power.html