DEV Community

Cristhiam
Cristhiam

Posted on • Originally published at streetcoder.dev on

An example of convivence between abstract classes and interfaces

Some years ago, someone asked me about of when to use an interface and when to use an abstract class. Well, the answer depends of some factors like common behaviors and level of extensibility. I thought it would be a good idea to express my answer by using an example

The example

For this example, we are going to create an imaginary concept of social network so for us a social network is something that can post messages with images and save an history of posted messages.

Creating a SocialNetwork abstract class

public abstract class SocialNetwork
{
    public List<string> History { get; private set; }

    protected SocialNetwork()
    {
        History = new List<string>();
    }

    public void Post(string comment, byte[] image)
    {
        DoPost(comment, image);
        History.Add(comment);
    }

    protected virtual void DoPost(string comment, byte[] image)
    {
    }
}

Enter fullscreen mode Exit fullscreen mode

Adding Facebook and Twitter social networks

public class Facebook : SocialNetwork
{
    protected override void DoPost(string comment, byte[] image)
    {
        //Logic to make a post on facebook
    }
}

public class Twitter : SocialNetwork
{
    protected override void DoPost(string comment, byte[] image)
    {
        //Logic to make a post on twitter
    }
}

Enter fullscreen mode Exit fullscreen mode

Fig. 1 Shows a diagram with the classes relationship my alternate textFig. 1

Now imagine that, we have a class that can post a message to all of our social networks:

public class SocialNetworkProcessor
{
    private readonly SocialNetwork[] _socialNetworks;

    public SocialNetworkProcessor(SocialNetwork[] socialNetworks)
    {
        _socialNetworks = socialNetworks;
    }

    public void Post(string message, byte[] image)
    {
        foreach (var socialNetwork in _socialNetworks)
        {
            socialNetwork.Post(message, image);
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

Putting all together

class Program
{
    static void Main(string[] args)
    {
        var socialNetworks = new SocialNetwork[]
        {
            new Facebook(), 
            new Twitter()
        };

        var processor = new SocialNetworkProcessor(socialNetworks);

        var someImage = new byte[] {1, 2, 3};
        processor.Post(&quot;Hello&quot;, someImage);
    }
}

Enter fullscreen mode Exit fullscreen mode

So far so good, but the code we have just written above has a serious design problem. Did you see the problem?. Imagine that, we have to handle a totally different kind of social network. E.g. something tha doesn’t store messages history, something like Snapchat, so our new class will be called Snapchat. Snapchat class will only keep a track of the latest posted message. Of course, we will pass our class by our SocialNetWorkProcessor, in order to post messages by our new class as well. Let me show you:

Creating Snapchat social network

public class Snapchat : SocialNetwork
{
    private string _lastMessage;

    protected override void DoPost(string comment, byte[] image)
    {
        //Logic to make a post on snapchat
        _lastMessage = comment;
        ProcessLastMessage();
        History.Clear();
    }

    private void ProcessLastMessage()
    {
        //Some logic here.
    }
}

Enter fullscreen mode Exit fullscreen mode

As you can notice above, Snapchat class inherits from SocialNetwork class, so Snapchat class will store a history of posts too. But we don’t want it, so we have to clear the history list

Fig. 2 shows the class diagram including the new Snapchat class

my alternate textFig. 2

Interfaces comes in action

The problem with the implementation shown above is that, Snapchat has a thing that he doesn’t need it, i.e. History. Imagine for a moment that, SocialNetwork class would has a lot of things like History, image history, timeline generator or whatever necessary for a normal social network. Every new social network that we would want to implent would be forced to inherit all of the current staff in SocialNetwork base class. That’s why we need a higher level of abstraction there. SocialNetwork base class is what we know how a normal social network, but we need a super abstraction to define what a SocialNetwork will do without define any behavior for it, so we need to define an interface

public interface ISocialNetwork
{
    void Post(string message, byte[] image);
}

Enter fullscreen mode Exit fullscreen mode

Now, we will do SocialNetwork class to implement ISocialNetwork

public abstract class SocialNetwork : ISocialNetwork
{
    public List<string> History { get; private set; }

    protected SocialNetwork()
    {
        History = new List<string>();
    }

    public void Post(string comment, byte[] image)
    {
        DoPost(comment, image);
        History.Add(comment);
    }

    protected virtual void DoPost(string comment, byte[] image)
    {
    }
}

Enter fullscreen mode Exit fullscreen mode

Here is the new Snapchat class:

public class Snapchat : ISocialNetwork
{
    private string _lastMessage;

    public void Post(string message, byte[] image)
    {
        //Logic to do a snapchat post
        _lastMessage = message;
        ProcessLastMessage();
    }

    private void ProcessLastMessage()
    {
        //Some logic here.
    }
}

Enter fullscreen mode Exit fullscreen mode

Fig 3. Shows the SocialNetwork and Snapchat classes implementing ISocialNetwork.

my alternate textFig. 3

Of course, we need to update our SocialNetworkProcessor class by replacing SocialNetwork class references by ISocialNetwork interface:

public class SocialNetworkProcessor
{
    private readonly ISocialNetwork[] _socialNetworks;

    public SocialNetworkProcessor(ISocialNetwork[] socialNetworks)
    {
        _socialNetworks = socialNetworks;
    }

    public void Post(string message, byte[] image)
    {
        foreach (var socialNetwork in _socialNetworks)
        {
            socialNetwork.Post(message, image);
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

So far, the design is powerfull enough, SocialNetworkProcessor can post in any social network that implements ISocialNetwork interface. Facebook and Twitter share common behavior from SocialNetwork and it implements ISocialNetwork. Snapchat class doesn’t share any behavior with Facebook and Twitter but it is a social network too, so it directly implements ISocialNetwork interface

A new player comes to the game

Now imagine that, we need to create a new social network e.g. Sobbr. Sobbr is a social network that only stores your posts by 24 hours. We also need a logic to process the lastPost of Sobbr like Snapchat does. By taking advantage of our current design, we can easily create a new class to share behavior between Snapchat and Sobbr, because those are volatile social networks, so our new base class will be called VolatileSocialNetwork and of course it will implement our ISocialNetwork interface

public abstract class VolatileSocialNetwork : ISocialNetwork
{
    private string _lastMessage;

    public void Post(string message, byte[] image)
    {
        //Logic to do a snapchat post
        _lastMessage = message;
        ProcessLastMessage();
    }

    private void ProcessLastMessage()
    {
        //Some logic here.
    }
}

Enter fullscreen mode Exit fullscreen mode

Creating the new class Sobbr

public class Sobbr : VolatileSocialNetwork
{
    private DateTime _creationTime;

    private void DestroySocialNetwork()
    {
        //Some logic here to destroy data after 24 hours.
    }
}

Enter fullscreen mode Exit fullscreen mode

Of course Snapchat will inherit from VolatileSocialNetwork too

public class Snapchat : VolatileSocialNetwork
{
}

Enter fullscreen mode Exit fullscreen mode

Fig 4. Shows the final design with Facebook and Twitter inherited from SocialNetwork base class, also Snapchat and Sobbr inherited from VolatileSocialNetwork base class. SocialNetwork and VolatileSocialNetwork implement ISocialNetwork interface

my alternate textFig. 4

The final

class Program
{
    static void Main(string[] args)
    {
        var socialNetworks = new ISocialNetwork[]
        {
            new Facebook(),
            new Twitter(),
            new Snapchat(),
            new Sobbr()
        };
        var processor = new SocialNetworkProcessor(socialNetworks);

        var someImage = new byte[] {1, 2, 3};
        processor.Post("Hello", someImage);
    }
}

Enter fullscreen mode Exit fullscreen mode

By following that design, we won’t to modify other parts of the code to include more social networks

Thanks for read

Top comments (0)