Yiğit Oğuz
Software Architect
30.05.2018
Related Digital Tech Talk
There are five general design principles of object-oriented programming intended to make software more understandable, extendable, maintainable and testable.
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
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);
}
}
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
}
}
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;
}
}
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();
}
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..
}
}
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);
}
}
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.
Object Oriented Design is the concept that forces programmers to plan out their code in order to have a better flowing program.
Language Consept
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)}");
}
}
}
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);
}
}
}