Clean Code

Yiğit Oğuz

Software Architect

30.05.2018

Related Digital Tech Talk

What is solid in software development?

There are five general design principles of object-oriented programming intended to make software more understandable, extendable, maintainable and testable.

 

  • Single responsibility principle
  • Open/closed principle
  • Liskov Substitution Principle
  • Interface Segregation Principle
  • Dependency Inversion Principle

 

Clean code is not written by following a set of rules. You don’t become a software craftsman by learning a list of heuristics. Professionalism and craftsmanship come from values that drive disciplines.

 

Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship

Single Responsibility Principle

A class should concentrate on doing one thing.
There should never be more than one reason for a class to change.
private void ValidateDwellCondition(NotifyOnEnteredEvent notifyOnEnteredEvent)
        {
            if (notifyOnEnteredEvent.IsDwell
                && notifyOnEnteredEvent.TargetSendTime > DateTime.Now)
            {
                string message = $"Invalid timing.Event:
                        {JsonConvert.SerializeObject(notifyOnEnteredEvent)}";
                _logger.Info(message);
                throw new Exception(message);
            }
        }

Open/Closed Principle

Change a class’ behaviour using inheritance and composition.
Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.
public interface INotificationSender
    {
        void Validate(NotifyOnEnteredEvent notifyOnEnteredEvent);
        void Send(NotifyOnEnteredEvent notifyOnEnteredEvent);
    }
public class PushNotificationSender : INotificationSender
    {
        public void Validate(NotifyOnEnteredEvent notifyOnEnteredEvent)
                {
                    ValidateDwellCondition(notifyOnEnteredEvent);
                    ValidateProfileId(notifyOnEnteredEvent);
                    ValidateOrganizationId(notifyOnEnteredEvent);
                    ValidateParams(notifyOnEnteredEvent);
                    ValidateMemcachedKey(notifyOnEnteredEvent);
                }
        public void Send(NotifyOnEnteredEvent notifyOnEnteredEvent)
        {
            //do something
        }
}

Liskov Substitution Principle

Subclasses should behave nicely when used in place of their parent class.
Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.
public class PushNotificationSender
    {
        private readonly INotifyPermissionProxy _notifyPermissionProxy;

        public PushNotificationSender(INotifyPermissionProxy notifyPermissionProxy)
        {
            _notifyPermissionProxy = notifyPermissionProxy;
        }
}

Liskov Substitution Principle ...

public class NotifyPermissionMemcachedProxy: INotifyPermissionProxy
    {
        private readonly IMemcachedClient _memcachedClient;

        public NotifyPermissionProxy(IMemcachedClient memcachedClient)
        {
            _memcachedClient = memcachedClient;
        }

        public string GetStatus(string key)
        {
            return _memcachedClient.Get<string>(key);
        }
    }
public class NotifyPermissionSqlProxy: INotifyPermissionProxy ..
private static void InjectDependencies(ContainerBuilder containerBuilder){

containerBuilder.RegisterType<NotifyPermissionMemcachedProxy>().As<INotifyPermissionProxy>()
//containerBuilder.RegisterType<NotifyPermissionSqlProxy>().As<INotifyPermissionProxy>()
.InstancePerLifetimeScope();
}

Interface Segregation Principle

Keep interfaces small and cohesive.
Clients should not be forced to depend upon interfaces that they do not use.
public interface INotificationSender
{
        void Validate(NotifyOnEnteredEvent notifyOnEnteredEvent);
        void Send(NotifyOnEnteredEvent notifyOnEnteredEvent);
}


public class SmsSender : INotificationSender
    {

        public void Validate(NotifyOnEnteredEvent notifyOnEnteredEvent)
        {
            throw new NotImplementedException();
        }

        public void Send(NotifyOnEnteredEvent notifyOnEnteredEvent)
        {
            //do something..
        }
}

Dependency Inversion Principle

Use lots of interfaces and abstractions.
High level modules should not depend upon low level modules.
public class PushNotificationSender : IPushNotificationSender
    {
        private readonly ILogger _logger;
        private readonly INotifyPermissionProxy _notifyPermissionProxy;
        private readonly INotifyProxy _notifyProxy;

        public PushNotificationSender(ILogger logger, INotifyProxy notifyProxy
, INotifyPermissionProxy notifyPermissionProxy)
        {
            _logger = logger;
            _notifyPermissionProxy = notifyPermissionProxy;
            _notifyProxy = notifyProxy;
        }
        
        public void Send(NotifyOnEnteredEvent notifyOnEnteredEvent)
        {
            //do something..
            _notifyProxy.SendPush(notifyOnEnteredEvent);
            
        }
}

Design Patterns

In software engineering, a design pattern is a general repeatable solution to a commonly occurring problem in software design. A design pattern isn't a finished design that can be transformed directly into code. It is a description or template for how to solve a problem that can be used in many different situations.

What is OO Design?

Object Oriented Design is the concept that forces programmers to plan out their code in order to have a better flowing program.

Language Consept

  • Encapsulation
  • Data Protection
  • Inheritance
  • Interface
  • Polymorphism

 

Why Unit Testing?

 

  • Makes the Process Agile
  • Quality of Code
  • Finds Software Bugs Early
  • Facilitates Changes and Simplifies Integration
  • Provides Documentation
  • Debugging Process
  • Design
  • Reduce Costs

 

Unit Testing Example

namespace Visilabs.NotifyService.Business.Imp
{
    public class PushNotificationSender : INotificationSender
    {
        private readonly ILogger _logger;
        private readonly INotifyPermissionProxy _notifyPermissionProxy;
        private readonly INotifyProxy _notifyProxy;

        public PushNotificationSender(ILogger logger, INotifyProxy notifyProxy
, INotifyPermissionProxy notifyPermissionProxy)
        {
            _logger = logger;
            _notifyPermissionProxy = notifyPermissionProxy;
            _notifyProxy = notifyProxy;
        }

        public void Validate(NotifyOnEnteredEvent notifyOnEnteredEvent)
        {
            //Code Something
        }
            
        public void Send(NotifyOnEnteredEvent notifyOnEnteredEvent)
        {
            string notifyPermissionStatus = GetNotifyPermissionStatus(notifyOnEnteredEvent);
    
            if (notifyPermissionStatus == AppConstants.OnEnter)
            {
                _notifyProxy.SendPush(notifyOnEnteredEvent);
    
                _logger.Info($"Notify Sended, Event: {JsonConvert.SerializeObject(notifyOnEnteredEvent)}");
            }
            else if (notifyPermissionStatus == AppConstants.OnExit)
                _logger.Info($"Users leave an area of interest,Event: {JsonConvert.SerializeObject(notifyOnEnteredEvent)},
NotifyPermissionStatus: {JsonConvert.SerializeObject(notifyPermissionStatus)}");
        }
    }
}

Unit Testing Example ...

namespace Visilabs.NotifyService.Business.Tests
{
    [TestClass]
    public class PushNotificationSenderTests
    {
        private IPushNotificationSender _sut;
        private Fixture _fixture;
        private Mock<ILogger> _mockedLogger;
        private Mock<INotifyProxy> _mockedNotifyProxy;
        private Mock<INotifyPermissionProxy> _mockedNotifyPermissionProxy;

        [TestInitialize]
        public void TestInit()
        {
            _fixture = new Fixture();
            _mockedLogger = new Mock<ILogger>();
            _mockedNotifyProxy = new Mock<INotifyProxy>();
            _mockedNotifyPermissionProxy = new Mock<INotifyPermissionProxy>();

            _sut = new Imp.PushNotificationSender(_mockedLogger.Object, _mockedNotifyProxy.Object,
_mockedNotifyPermissionProxy.Object);
        }

        [TestMethod]
        public void GetNotifyPermissionStatus_RetrunOnEnter_ShouldSendPush()
        {
            string memcachedKey = _fixture.Create<string>();
            string memcachedValue = AppConstants.OnEnter;
            _mockedNotifyPermissionProxy.Setup(mmc => mmc.GetStatus(memcachedKey)).Returns(memcachedValue);

            NotifyOnEnteredEvent dummyNotifyOnEnteredEvent = _fixture.Build<NotifyOnEnteredEvent>()
                .With(noee => noee.IsDwell, true)
                .With(noee => noee.TargetSendTime, DateTime.Now.AddHours(-_fixture.Create<int>()))
                .With(noee => noee.MemcachedKey, memcachedKey).Create();

            _sut.Send(dummyNotifyOnEnteredEvent);
            Assert.IsTrue(true);
        } 
    }
}
Made with Slides.com