C ා practice design pattern principle solid


The relationship between theory and practice is not far away, but near. It’s hard to say whether we can apply theory to practice.

When it comes to design patterns, one of the most common principles is solid

  1. S – single responsibility principle
  2. O – Open Closed Principle
  3. L – Liskov Substitution Principle
  4. I – Interface Segregation Principle
  5. D – Dependency Inversion Principle

Well, these are the five principles.

Later, another law of Demeter was added. So it became the six principles.

The principle is easy to understand. How to use it in practice?

    In order to prevent the reprint of the original website, the original link is added here: https://www.cnblogs.com/tiger-wang/p/13525841.html

1、 Single responsibility principle

The principle of single responsibility, in short, is a class or a module, responsible for only one or a kind of responsibilities.

Look at the code:

public interface IUser{    void AddUser();    void RemoveUser();    void UpdateUser();    void Logger();    void Message();}

According to the principle, we will find that forIUserFor example, the first three methods:AddUserRemoveUserUpdateUserIt makes sense. The last twoLoggerandMessageAsIUserPart of the functions of the system are meaningless and do not conform to the principle of single responsibility.

Therefore, we can decompose it into different interfaces:

public interface IUser{    void AddUser();    void RemoveUser();    void UpdateUser();}public interface ILog{    void Logger();}public interface IMessage{    void Message();}

After splitting, we can see that the three interfaces fulfill their own responsibilities, and the readability and maintainability are very good.

The following is an example of using dependency injection:

public class Log : ILog{    public void Logger()    {        Console.WriteLine("Logged Error");    }}public class Msg : IMessage{    public void Message()    {        Console.WriteLine("Messaged Sent");    }}class Class_DI{    private readonly IUser _user;    private readonly ILog _log;    private readonly IMessage _msg;    public Class_DI(IUser user, ILog log, IMessage msg)    {        this._user = user;        this._log = log;        this._msg = msg;    }    public void User()    {        this._user.AddUser();        this._user.RemoveUser();        this._user.UpdateUser();    }    public void Log()    {        this._log.Logger();    }    public void Msg()    {        this._msg.Message();    }}public static void Main(){    Class_DI di = new Class_DI(new User(), new Log(), new Msg());    di.User();    di.Log();    di.Msg();}

This code, looks much more beautiful.

2、 Open close principle

The open close principle requires that entities such as classes, modules and functions should be open to extensions and closed to modifications.

Let’s take a look at a code to calculate the employee’s bonus:

public class Employee{    public int Employee_ID;    public string Name;    public Employee(int id, string name)    {        this.Employee_ID = id;        this.Name = name;    }    public decimal Bonus(decimal salary)    {        return salary * .2M;    }}class Program{    static void Main(string[] args)    {        Employee emp = new Employee(101, "WangPlus");        Console.WriteLine("Employee ID: {0} Name: {1} Bonus: {2}", emp.Employee_ID, emp.Name, emp.Bonus(10000));    }}

Now suppose that the formula for calculating the bonus has been changed.

To achieve this, we may need to modify the code:

public class Employee{    public int Employee_ID;    public string Name;    public string Employee_Type;    public Employee(int id, string name, string type)    {        this.Employee_ID = id;        this.Name = name;        this.Employee_Type = type;    }    public decimal Bonus(decimal salary)    {        if (Employee_Type == "manager")            return salary * .2M;        else            return                salary * .1M;    }}

Obviously, in order to implement the change, we changed the classes and methods.

This is against the principle of opening and closing.

So what do we do?

We can use abstract classes to implement them – of course, there are many ways to implement them. Choose the most habitual or natural way

public abstract class Employee{    public int Employee_ID;    public string Name;    public Employee(int id, string name)    {        this.Employee_ID = id;        this.Name = name;    }    public abstract decimal Bonus(decimal salary);}

Then we implement the original function:

public class GeneralEmployee : Employee{    public GeneralEmployee(int id, string name) : base(id, name)    {    }    public override decimal Bonus(decimal salary)    {        return salary * .2M;    }}class Program{    public static void Main()    {        Employee emp = new GeneralEmployee(101, "WangPlus");        Console.WriteLine("Employee ID: {0} Name: {1} Bonus: {2}", emp.Employee_ID, emp.Name, emp.Bonus(10000));    }}

The advantage of using abstract classes here is that if you need to modify the bonus rules in the future, you don’t need to modify the entire class and method as in the previous example, because the current extension is open.

The code is complete, as follows:

public abstract class Employee{    public int Employee_ID;    public string Name;    public Employee(int id, string name)    {        this.Employee_ID = id;        this.Name = name;    }    public abstract decimal Bonus(decimal salary);}public class GeneralEmployee : Employee{    public GeneralEmployee(int id, string name) : base(id, name)    {    }    public override decimal Bonus(decimal salary)    {        return salary * .1M;    }}public class ManagerEmployee : Employee{    public ManagerEmployee(int id, string name) : base(id, name)    {    }    public override decimal Bonus(decimal salary)    {        return salary * .2M;    }}class Program{    public static void Main()    {        Employee emp = new GeneralEmployee(101, "WangPlus");        Employee emp1 = new ManagerEmployee(102, "WangPlus1");        Console.WriteLine("Employee ID: {0} Name: {1} Bonus: {2}", emp.Employee_ID, emp.Name, emp.Bonus(10000));        Console.WriteLine("Employee ID: {0} Name: {1} Bonus: {2}", emp1.Employee_ID, emp1.Name, emp1.Bonus(10000));    }}

3、 Ritz’s substitution principle

The principle of Richter substitution is that subclasses can extend the functions of the parent class, but cannot change the original functions of the base class. It has four meanings

  1. The subclass can implement the abstract method of the parent class, but can not override the non abstract method of the parent class;
  2. You can add your own unique methods in subclass;
  3. When the subclass overloads the method of the parent class, the preconditions (formal parameters) of the method are more relaxed than the input parameters of the parent class;
  4. When the subclass implements the abstract method of the parent class, the post condition (return value) of the method is stricter than that of the parent class.

In the former open close principle, in our example, we also follow the partial Ritz substitution principleGeneralEmployeeandManagerEmployeeReplaced the parent classEmployee

Take the code.

Assuming that the demand has changed, a temporary worker is added this time, and there is no bonus.

public class TempEmployee : Employee{    public TempEmployee(int id, string name) : base(id, name)    {    }    public override decimal Bonus(decimal salary)    {        throw new NotImplementedException();    }}class Program{    public static void Main()    {        Employee emp = new GeneralEmployee(101, "WangPlus");        Employee emp1 = new ManagerEmployee(101, "WangPlus1");        Employee emp2 = new TempEmployee(102, "WangPlus2");        Console.WriteLine("Employee ID: {0} Name: {1} Bonus: {2}", emp.Employee_ID, emp.Name, emp.Bonus(10000));        Console.WriteLine("Employee ID: {0} Name: {1} Bonus: {2}", emp1.Employee_ID, emp1.Name, emp1.Bonus(10000));        Console.WriteLine("Employee ID: {0} Name: {1} Bonus: {2}", emp2.Employee_ID, emp2.Name, emp2.Bonus(10000));        Console.ReadLine();    }}

Obviously, this method does not conform to the fourth principle of the Richter’s principle, and it throws a mistake.

Therefore, we need to continue to modify the code and add two interfaces:

interface IBonus{    decimal Bonus(decimal salary);}interface IEmployee{    int Employee_ID { get; set; }    string Name { get; set; }    decimal GetSalary();}public abstract class Employee : IEmployee, IBonus{    public int Employee_ID { get; set; }    public string Name { get; set; }    public Employee(int id, string name)    {        this.Employee_ID = id;        this.Name = name;    }    public abstract decimal GetSalary();    public abstract decimal Bonus(decimal salary);}public class GeneralEmployee : Employee{    public GeneralEmployee(int id, string name) : base(id, name)    {    }    public override decimal GetSalary()    {        return 10000;    }    public override decimal Bonus(decimal salary)    {        return salary * .1M;    }}public class ManagerEmployee : Employee{    public ManagerEmployee(int id, string name) : base(id, name)    {    }    public override decimal GetSalary()    {        return 10000;    }    public override decimal Bonus(decimal salary)    {        return salary * .1M;    }}public class TempEmployee : IEmployee{    public int Employee_ID { get; set; }    public string Name { get; set; }    public TempEmployee(int id, string name)    {        this.Employee_ID = id;        this.Name = name;    }    public decimal GetSalary()    {        return 5000;    }}class Program{    public static void Main()    {        Employee emp = new GeneralEmployee(101, "WangPlus");        Employee emp1 = new ManagerEmployee(102, "WangPlus1");        Console.WriteLine("Employee ID: {0} Name: {1} Salary: {2} Bonus:{3}", emp.Employee_ID, emp.Name, emp.GetSalary(), emp.Bonus(emp.GetSalary()));        Console.WriteLine("Employee ID: {0} Name: {1} Salary: {2} Bonus:{3}", emp1.Employee_ID, emp1.Name, emp1.GetSalary(), emp1.Bonus(emp1.GetSalary()));        List emp_list = new List();        emp_list.Add(new GeneralEmployee(101, "WangPlus"));        emp_list.Add(new ManagerEmployee(102, "WangPlus1"));        emp_list.Add(new TempEmployee(103, "WangPlus2"));        foreach (var obj in emp_list)        {            Console.WriteLine("Employee ID: {0} Name: {1} Salary: {2} ", obj.Employee_ID, obj.Name, obj.GetSalary());        }    }}

4、 Interface isolation principle

The principle of interface isolation requires that the client does not rely on the interfaces and methods it does not use; the dependency of one class on another should be based on the smallest interface.

The common approach is to split a bloated interface into smaller interfaces to ensure that customers only need to know the methods related to it.

There is no code demonstration in this part. You can take a look at the code in the single responsibility principle above, which also follows this principle.

5、 The principle of Dependence Inversion

The principle of dependency inversion requires that high-level modules should not rely on low-level modules, but both depend on abstraction. In addition, abstraction should not depend on details, and details should depend on abstractions.

Look at the code:

public class Message{    public void SendMessage()    {        Console.WriteLine("Message Sent");    }}public class Notification{    private Message _msg;    public Notification()    {        _msg = new Message();    }    public void PromotionalNotification()    {        _msg.SendMessage();    }}class Program{    public static void Main()    {        Notification notify = new Notification();        notify.PromotionalNotification();    }}

In this code, the notification is completely dependentMessageClass, andMessageClass can send only one notification. If we need to introduce other types, such as mail and SMS, we need to modify themMessageClass.

Next, we use the dependency inversion principle to complete this Code:

public interface IMessage{    void SendMessage();}public class Email : IMessage{    public void SendMessage()    {        Console.WriteLine("Send Email");    }}public class SMS : IMessage{    public void SendMessage()    {        Console.WriteLine("Send Sms");    }}public class Notification{    private IMessage _msg;    public Notification(IMessage msg)    {        this._msg = msg;    }    public void Notify()    {        _msg.SendMessage();    }}class Program{    public static void Main()    {        Email email = new Email();        Notification notify = new Notification(email);        notify.Notify();        SMS sms = new SMS();        notify = new Notification(sms);        notify.Notify();    }}

In this way, we minimize the coupling between the code.

6、 Dimitar’s law

Dimitar’s law is also called the least known rule. From the address we can know, meaning: an object should have the least understanding of other objects.

When writing code, expose your own interfaces or methods as little as possible. When writing classes, can youpublicNopublicAll exposed properties, interfaces and methods must be exposed to ensure that other classes have a minimum understanding of this class.

There is nothing to be said about this principle. The caller only needs to know the method disclosed by the callee. As for how it is implemented internally or there are other methods, the caller does not care. The caller only cares about what it needs to be used. On the contrary, if the callee exposes too many properties or methods that do not need to be exposed, the callers may abuse the methods or cause some other unnecessary troubles.

Finally, let’s say a few words: the so-called principles are not rules or rigid regulations. In the code, it is good to be flexible in application, and it does not need to be formalized. However, if it is used well, it will make the code easy to write and beautiful.

(end of full text)



WeChat official account: Lao Wang Plus

Scanning the two-dimensional code, paying attention to the official account, can get the latest personal articles and content push.

The copyright of this article belongs to the author. Please keep this notice and the original link

Recommended Today

JS function

1. Ordinary function Grammar: Function function name (){ Statement block } 2. Functions with parameters Grammar: Function function name (parameter list){ Statement block } 3. Function with return value Grammar: Function function name (parameter list){ Statement block; Return value; } Allow a variable to accept the return value after calling the function Var variable name […]