3

I'm starting work a new personal project to send out emails using different email services (like mailchimp, for example). I just started coding and I have a base class called Email:

public class Email
{
    private string _to;
    private string _from;
    private string _body;
    private string _templateName;

    public Email(string to, string from, string body, string templateName)
    {
        this._to = to;
        this._from = from;
        this._body = body;
        this._templateName = templateName;
    }
}

an interface called IEmail:

public interface IEmail
{     
    string Send();
}

and a class called MailChimpEmail that will send out emails using MailChimps API and that inherits from Email and implements the IEmail interface

public class MailChimpEmail : Email, IEmail
{
    private string _body;
    private string _to;
    private string _from;
    private string _templateName;

    public MailChimpEmail (string to, string from, string body, string templateName) : base(to, from, body, templateName)
    {
        this._to = to;
        this._from = from;
        this._body = body;
        this._templateName = templateName;
    }

    public string Send()
    {            
        throw new NotImplementedException();
    }
}

I plan on being able to use another 3rd party email sender so I'm not tied to one and will create a new class and implement the IEmail interface and inherit Email. But, my question is: do I really need to inherit Email? Say the classes don't gain any new functionality and stay as such (so I can just Send an email, that's it), what am I gaining by inheriting the base class if I'm implementing the interface? The Send method will have a different implementation for each class, so what would the point be?

  • 2
    That is a weird design. Why not have: Email(Just properties(DTO), IEmailSender with method Send(Email). It is weird because an email in general don't send it self. – Esben Skov Pedersen Nov 18 '15 at 6:53
5

An interface and a parent (or base) class solve different problems, so it depends on the problem you're trying to solve.

An interface defines a contract. This is handy when you want to manipulate a variety of concrete implementations in a consistent, well-defined manner. They're great for hiding the gory details that probably don't matter in the consumer. They're also good at forcing us to think about useful abstractions.

A base class also offers a contract of sorts (any number of extended classes can be handled as if they are the base class), but its most useful feature is its shared, concrete implementation. If you have implementation that's identical in a family of related classes, you can DRY up your code with a base class.

At first glance, it looks like your example only requires the interface. There's no obvious shared implementation. Looking closer, though, I wonder. Both Email and MailChimpEmail define a body, addresses, and a template. It might be worth defining those once and only once, especially if there's any tricky state management. If each email type must define its own Send(), an abstract base class might better convey the intent (the base class isn't instantiate-able). You get a little contract and a little concrete implementation.

public abstract class BaseEmail
{
    public string Body { get; set; }
    public string From { get; set; }
    public string To { get; set; }
    public string Template {get; set; }

    protected BaseEmail (string body, string from, string to, string template)
    {
        this.Body = body;
        this.From = from;
        this.To = to;
        this.Template = template;
    }

    public abstract string Send();
}

public class VanillaEmail : BaseEmail
{
    public VanillaEmail (string body, string from, string to, string template)
        : base (body, from, to, template)
    {
    }

    public override string Send()
    {
        // do some vanilla work
    }
}

public class MailChimpEmail : BaseEmail
{
    public MailChimpEmail (string body, string from, string to, string template)
        : base (body, from, to, template)
    {
    }

    public override string Send()
    {
        // do some mailchimp work
    }
}

Keep in mind that this is only one way to model the problem. Some would argue that an email shouldn't be able to send itself. In this case, you can separate the concern of sending an email message from the message itself.

public class Email
{
    public string Body { get; set; }
    public string From { get; set; }
    public string To { get; set; }
    public string Template {get; set; }
}

public interface IMailer
{
    string Send(Email email);
}

public class VanillaMailer : IMailer
{
    public string Send(Email email)
    {
        // do some vanilla work
    }
}

public class MailChimpMailer : IMailer
{
    public string Send(Email email)
    {
        // do some mailchimp work
    }
}
1

We should inherit if there is a 'is a relation'. We should use aggregation if there is a 'has a relation'.

There is no need to inherit 'Email' class in every email sending implementation classes. Your 'Email' class is a model class. Email sending logic implementation classes should only implement the 'IEmail' interface and use 'Email' type private variable inside them.

public class Email
{
    private string _to;
    private string _from;
    private string _body;
    private string _templateName;

    public Email(string to, string from, string body, string templateName)
    {
        this._to = to;
        this._from = from;
        this._body = body;
        this._templateName = templateName;
    }
}

public interface IEmail
{
    string Send();
}

public class MailChimpEmail : IEmail
{
    private Email _email;

    public MailChimpEmail(Email email)
    {
        this._email = email;
    }

    public string Send()
    {
        // implementation goes here...
    }
}

public class ThirdPartyEmail : IEmail
{
    private Email _email;

    public ThirdPartyEmail(Email email)
    {
        this._email = email;
    }

    public string Send()
    {
        // Third party implementation goes here...
    }
}

'MailChimpEmail' and 'ThirdPartyEmail' implement 'IEmail' interface and give its own logic to the 'send()' method. 'MailChimpEmail' and 'ThirdPartyEmail' is in 'has a relation' with 'Email' class, so it should not inherit rather it should use 'Email' type variable inside them to work with (this is called aggregation).

  • i would suggest MailChimpEmail and ThirdPartyEmail take Email argument for Send() method instead of taking Email as dependency in constructor. Reason being people expect an email sender able to send multiple emails, not tied to a specific email. Also, if taking Email in constructor, it requires the creator (.e.g. a factory) to pass in the email to send. That responsibility better to be delegated to the client of Send() method – Phuong Nguyen Nov 18 '15 at 4:07
  • In that case we can create a static class with static 'Send()' method in it. – DeveloperArnab Nov 18 '15 at 9:12
0

You are correct, you are not gaining anything by inheriting Email. In fact, the class Email right now is completely useless. Using only the interface is just fine. Optionally, you could use an abstract class instead of an interface but either will do.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.