Difference between abstract class and interface with a practical example

Do you know when use an Abstract Class instead of Interface?

What are the differences? How to describe them?

What about a practical example, implementing a repository based on the Dapper example?

First of all, the concept.

  • Abstract class can not be build. A command like new MyAbstractClass() is not allowed. But I can define a constructor to it. Why? Because it will be called when you initiate a new instance of some class that¬†inherit it.
  • Interface just define methods and properties signatures. It works like a contract. This way, I can assure that everybody who implements it, will have the expected behavior.

Now the example:

using Dapper;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;

namespace BlogPoc
{
    class Program
    {
        static void Main(string[] args)
        {
            // get new repository instance
            IBlogPostRepository repository = new BlogPostRepository();

            // insert
            var newEntity = new BlogPost { Title = "How to implement a repository", Description = "In this post we will talk about repositories" };
            repository.Insert(newEntity);

            //select
            var posts = repository.Query();

            //total of comments of the first post
            var totalCommentsOfFirstPost = repository.TotalComments(1);

            foreach (var post in posts)
                Console.WriteLine($"{post.Title} - {post.Description}");

            Console.WriteLine();
            Console.WriteLine($"The first post has {totalCommentsOfFirstPost} comments");

            Console.WriteLine();
            Console.WriteLine("end...");
            Console.ReadLine();
        }
    }

    public class Comment
    {
        public int Id { get; set; }
        public string Message { get; set; }
        public BlogPost BlogPost { get; set; }
    }

    public class BlogPost
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public List<Comment> Comments { get; set; }
    }

    public abstract class Repository<T>
    {
        protected string connectionString = @"Data Source=(LocalDb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\BlogTestDatabase.mdf;Initial Catalog=BlogTestDatabase;Integrated Security=True";

        public void Insert(T entity)
        {
            var columns = GetColumns();
            var stringOfColumns = string.Join(", ", columns);
            var stringOfParameters = string.Join(", ", columns.Select(e => "@" + e));
            var query = $"insert into {typeof(T).Name}s ({stringOfColumns}) values ({stringOfParameters})";

            using (var connection = new SqlConnection(connectionString))
            {
                connection.Open();
                connection.Execute(query, entity);
            }
        }

        public void Delete(T entity)
        {
            var query = $"delete from {typeof(T).Name}s where Id = @Id";

            using (var connection = new SqlConnection(connectionString))
            {
                connection.Open();
                connection.Execute(query, entity);
            }
        }

        public void Update(T entity)
        {
            var columns = GetColumns();
            var stringOfColumns = string.Join(", ", columns.Select(e => $"{e} = @{e}"));
            var query = $"update {typeof(T).Name}s set {stringOfColumns} where Id = @Id";

            using (var connection = new SqlConnection(connectionString))
            {
                connection.Open();
                connection.Execute(query, entity);
            }
        }

        public IEnumerable<T> Query(string where = null)
        {
            var query = $"select * from {typeof(T).Name}s ";

            if (!string.IsNullOrWhiteSpace(where))
                query += where;

            using (var connection = new SqlConnection(connectionString))
            {
                connection.Open();
                return connection.Query<T>(query);
            }
        }

        private IEnumerable<string> GetColumns()
        {
            return typeof(T)
                    .GetProperties()
                    .Where(e => e.Name != "Id" && !e.PropertyType.IsGenericType)
                    .Select(e => e.Name);
        }
    }

    public interface IBlogPostRepository
    {
        void Insert(BlogPost entity);
        void Delete(BlogPost entity);
        void Update(BlogPost entity);
        IEnumerable<BlogPost> Query(string where = null);
        int TotalComments(int blogPostId);
    }

    public interface ICommentRepository
    {
        void Insert(Comment entity);
        void Delete(Comment entity);
        void Update(Comment entity);
        IEnumerable<Comment> Query(string where = null);
    }

    public class BlogPostRepository : Repository<BlogPost>, IBlogPostRepository
    {
        public int TotalComments(int blogPostId)
        {
            var query = $"select count(*) from Comments where BlogPost_Id = @Id ";

            using (var connection = new SqlConnection(connectionString))
            {
                connection.Open();
                return connection.ExecuteScalar<int>(query, new { Id = blogPostId });
            }
        }
    }

    public class CommentRepository : Repository<Comment>, ICommentRepository
    {
    }
}

Lets start taking a loot at Interfaces: they are small, with a couple of lines, usually saying the methods signatures. But if we want more, like put some code? OK now we can create a class who will define the common behavior.

In this example, a repository can be about anything. But is does make sense have a “new Repository()“? No because repositories usually works with an entity.

But I could have a “new Repository<BlogPost>()“? It could make sense, but how could we handle with the specific operations, like “TotalComments()“?

You can say “in this case I need to inherit from repository, but I have no problems with the repository for the comments”. And I’ll say: “When you have a system with 50 entities it will become a mess, with some direct access to repository and other with its own inheritance”.

So, you could ask me: How can I avoid that?

Defining the repository as abstract! This way nobody can get a direct instance. And now we create a pattern, obeying everybody to have its own implementation of the repository, opening space to necessary customization.

Another doubt in your mind at this point: OK, but what is the reason of the Interfaces?

Now we are working with the Dapper, but if I decide to work with ADO.NET? Or even with NHibernate?

I can change just the repository implementations, following the “contract” established by Interfaces. If you use IoC and a Dependence Injection Container, the change between different implementations of the repository will be easy to do.

And I have more to say: How could you implement a title not empty validation in the repository?

Now I have a new concept, a virtual method! It can be modified, overridden.

Going back in the insert example of the repository, we need to add virtual to the signature (there is no need to do that in the Interfaces!):

public abstract class Repository<T>
{
    public virtual void Insert(T entity)
    {
        var columns = GetColumns();
        ... rest of the implemetation

And in the BlogPostRepository we can override it and call it after our new logic:

public class BlogPostRepository : Repository<BlogPost>, IBlogPostRepository
{
    public override void Insert(BlogPost entity)
    {
        if (string.IsNullOrWhiteSpace(entity.Title))
            throw new Exception("The title can be empty!");

        base.Insert(entity);
    }

    ... rest of the implementation

I think this example is something more than the classical examples of the geometrical shapes that we usually learn in the OO studies.

Spaki.

With more than 15 years of experience developing softwares and technologies, talking about startups, trends and innovation, today my work is focused to be CTO, Software Architect, Technical Speaker, Technical Consultant and Entrepreneur.

From Brazil, currently lives in Portugal working at https://www.farfetch.com as Software Architect, besides to keep projects in Brazil, like http://www.almocando.com.br/

Share

Leave a Reply

Your email address will not be published. Required fields are marked *