SOLID Principles - gablesiak/learning-path GitHub Wiki
-class should only have one responsibility
-class should only have one reason to change
-amount of classes in project increases, but it makes it easier to read/maintain
-class with one responsibility will have far fewer test cases
-less functionality in a single class will have fewer dependencies
-atomic classes -> impossible to reduce
Wrong code example:
class Person
{
public string Name { get; set; }
public string Lastname { get; set; }
public string City { get; set; }
public string Street { get; set; }
public int HouseNumber { get; set; }
public string Email { get; set; }
public Person(string name, string lastname, string email)
{
Name = name;
Lastname = lastname;
Email = ValidateEmail(email);
}
private string ValidateEmail(string email)
{
if (!email.Contains("@") || !email.Contains("."))
{
throw new FormatException("Email address has a wrong format!");
}
return email;
}
}
Errors:
-contains mail validation method, it shouldn't be a responsibility of this particular class
-it should not contain attributes that are not related to it - address
-problem with address validation in other places - it'd require to use class person
-it'd be necessary to copy the code in many places
Corrected version:
class Address
{
public string City { get; set; }
public string Street { get; set; }
public int HouseNumber { get; set; }
}
class Person
{
public string Name { get; set; }
public string Lastname { get; set; }
public string Email { get; set; }
public Address PersonAddress { get; set; }
public Person(string name, string lastname, string email)
{
Name = name;
Lastname = lastname;
Email = email;
}
}
class EmailValidator
{
public void ValidateEmail(string email)
{
if (!email.Contains("@") || !email.Contains("."))
{
throw new FormatException("Email address has a wrong format!");
}
}
-splited into 3 classes
-each class have single responsibility
-classes should be open for extension but closed for modification
-it stops from modifying existing code and causing potential new issues
-exception - fixing bugs in existing code
Wrong code example:
class Square
{
public int A { get; set; }
}
class Rectangle
{
public int A { get; set; }
public int B { get; set; }
}
class Calculator
{
public int Area(object shape)
{
if (shape is Square)
{
Square square = (Square)shape;
return square.A * square.A;
}
else if (shape is Rectangle)
{
Rectangle rectangle = (Rectangle)shape;
return rectangle.A * rectangle.B;
}
return 0;
}
}
Errors:
-adding a new shape => class modification
Corrected version:
abstract class Shape
{
public abstract int Area();
}
class Square : Shape
{
public int A { get; set; }
public override int Area()
{
return A * A;
}
}
class Rectangle : Shape
{
public int A { get; set; }
public int B { get; set; }
public override int Area()
{
return A * B;
}
}
class Calculator
{
public int Area(Shape shape)
{
return shape.Area();
}
}
-Base class methods are used correctly for inheritance
-Plan and use inheritance so that derived classes can use all the methods from the base class.
-the rule is broken most often with too general polymorphism
-derived classes override the base class's methods, replacing its mismatched logic
Wrong code example:
public abstract class Bird
{
public void Fly() { }
}
public class Dove : Bird
{
public void Fly()
{
// code
}
}
public class Penguin : Bird
{
public void Fly()
{
// code
}
}
Errors:
-if we force the use of the base class, the inheriting classes will be affected with logical errors
Corrected version:
{
public void Fly() { }
}
public abstract class WalkingBird
{
public void Walk() { }
}
public class Dove : FlyingBird
{
public void Fly()
{
// tresc metody w klasie gołąb
}
}
public class Penguin : WalkingBird
{
public void Walk()
{
// tresc metody w klasie pingwin
}
-Base class methods are used correctly for inheritance
-orderliness in interfaces
-maximum atomicity
-simplification of interface/base class
-more dedicated interfaces better than one general
-future issues with inheritance
-problems with methods that are later unnecessary in other code snippet
Wrong code example:
public interface IAccount
{
public void CreateUser() { }
public void EditUser() { }
public void DeleteUser() { }
public void ValidateLoginUser() { }
}
public class Register : IAccount
{
public void CreateUser()
{ // tresc metody}
public void EditUser()
{ throw new NotImplementedException();}
public void DeleteUser()
{ throw new NotImplementedException(); }
public bool ValidateLoginUser()
{ // tresc metody }
}
public class UserProfile : IAccount
{
public void CreateUser()
{ throw new NotImplementedException(); }
public void EditUser()
{ // tresc metody }
public void DeleteUser()
{ throw new NotImplementedException();}
public bool ValidateLoginUser()
{ throw new NotImplementedException();}
}
Errors:
-IAccount contains 4 methods
-Classes Register i UserProfile inherit all methods from interface, not all of them are necessary
-this forces an addition of exception
Corrected version:
{
public void CreateUser() { }
}
public interface IEditU
{
public void EditUser() { }
}
public interface IDeleteU
{
public void DeleteUser() { }
}
public interface IValidateU
{
public void ValidateLoginUser() { }
}
public class Register : ICreateU, IValidateU
{
public void CreateUser()
{
// code
}
public bool ValidateLoginUser()
{
// code
}
}
public class UserProfile : IEditU
{
public void EditUser()
{
// code
}
}
-inheritance added as needed
-smaller interfaces
-all dependencies should depend as much as possible on abstraction and not on a specific type
-dependence, e.g. inheritance
-business logic classes should not be dependent on non-core classes
Wrong code example:
{
public SMS sendSms { get; set; }
public MAIL sendMail { get; set; }
public Message()
{
sendSms = new SMS();
sendMail = new MAIL();
}
public void SendMessages()
{
sendSMS.Send();
sendMail.Send();
}
}
Errors:
-class message creates 2 objects in constructor, based on SMS and mail
-the upper-level class depends on the lower-level classes
-sendmessage – methods from 2 related classes
Corrected version:
{
public void Send();
}
public class SMS : IMessage
{
public void Send()
{
// treść metody obsługująca SMS
}
}
public class MAIL : IMessage
{
public void Send()
{
// treść metody obsługująca MAIL
}
}
public class Message
{
private IEnumerable<IMessage> allMessages;
public Messenger(IEnumerable<IMessage> messages)
{
allMessages = messages;
}
public void Send()
{
allMessages.AsEnumerable().ToList().ForEach(n => n.Send());
}
}
-abstraction – interface defines send method
-dependencies between high and low level classes removed