In the last two weeks I need to expand on a legacy of history, just as a lot of people don't want to touch the legacy code, and my heart is just as resistant to this task. These codes are filled with various null judgments (are you writing the return null correctly?). ), non-canonical variable names, layered nested if...else statements. Obviously I don't know what to do with this code, let alone refactoring, unit testing. What I need is to try not to move the previous code, and to be careful with the meaning of the If...else statement, I have no way of taking care of the next maintainer's feelings.
The reason for today's situation is not that the old code does not use polymorphism, inheritance, encapsulation, and not the previous use of design patterns, in my opinion the fundamental reason is that these historical legacy code does not conform to the principle of single responsibility (SRP), there is no reasonable abstraction.
When I look at the code from the perspective of these legacy code authors, I can't complain about anything because I personally write a lot of code that is not SRP, and I haven't been able to write the SRP code for a long time, and I don't have the flexibility to apply this guideline, although I know what the SRP is all about. It seems that the object-oriented principles seem simple, but only through a lot of "deliberate" practice can we grasp the essence of them. The word "deliberately" is to show that just immersing yourself in the business without thinking about coding does not improve the ability to object-oriented.
I am fortunate that my master often hangs on the lips of the solid principle, reminding us again and again how important the SRP is, constantly review my code, helping me to find my own self-awareness problems, and after a period of practice, I am now able to skillfully write the SRP code.
A simple guideline for the SRP is to write as short a class as possible. don't worry that the number of classes is growing exponentially, and experience tells us that a lot of good file structures and well-named analogies with only one file and thousands of lines of class are better for reading and understanding.
One might ask, do I fit the SRP in a class that contains only two of the methods? Or is there a hundreds of-line class in the BCL that doesn't fit the SRP? Is there a simple and reliable way to write the SRP code?
Let's write a class called "User Registry" in C #:
public class Userregistry {public void Register (user user) { if (isinvalidemail. (Email)) throw new Exception (string. Format ("Invalid email:{0}", user. Email)); if (Isinvalidphonenumber (user. Phone)) throw new Exception (string. Format ("Invalid Phone number:{0}", user. Phone)); New Userrepository (). Save (user); } private bool Isinvalidemail (string email) { //verify this email return false; } private bool Isinvalidphonenumber (string phonenumber) { //verify this phonenumber return false; } }
Does this class conform to the SRP principle? Let's see if objective-c can be one of our ideas:
Use Objective-c to output "Hello World":
@implementation greeter//defines a SayHello method-(void) sayhello{ NSLog (@ "Hello World");} @end//Send Alloc message to greeter, send init message, get a greeter instance greeter *greeter=[[greeter alloc] init]; Send SayHello message to Greeter Object [Greeter SayHello];
As you can see, "Send Message" in objective-c instead of "Method call". Although they end up being the same, the idea of "sending messages" requires our careful taste:
1. Sending a message to an object means that the objects communicate with each other through messages and are more loosely coupled.
2. Sending a message to an object seems to be a conversation between objects, making the object more alive, and having vitality to make it easier to judge the object's responsibilities.
3, "method call" means I need to know what you can do, and then call it without hesitation; "Send Message" means I know what you are capable of, I send you a message outside your ability, you may not respond.
User registration classes written with Objective-c:
@implementation userregistry-(void) REG: (User *) user{BOOL *isinvalidemail=[self isinvalidemail:user.email];// ① send isinvalidemail message to User Center BOOL *isinvalidphone=[self Isinvalidphone:user.phone];//② send isinvalidphone message to User Center//generation below The code is used to throw an exception without caring if (isinvalidemail) {nsexception *invalidemailexception=[nsexception exceptionwithname:@ "RegUser Exception "reason:@" Invalid Email " Userinfo:nil]; @throw invalidemailexception; } if (Isinvalidphone) {nsexception *invalidphoneexception=[nsexception exceptionwithname:@ "RegUserException" reason:@ "Invalid Phone" Userinfo:nil]; @throw invalidphoneexception; } userrepository *userrepository=[[userrepository Alloc] init];//initialize a Userrepository object [Userrepository Savewithuser:user];//③ sends Savewithuser message to Userrepository}-(BOOL *) Isinvalidemail: (NSString *) email{R Eturn false;} -(BOOL *) Isinvalidphone: (NSString *) phone{return false;} @end
Because the code coloring tool cannot color the code above, a friend who is unwilling to read this code only needs to understand how the OBJECTIVE-C uses the send message instead of the method call. Let's read this piece of code in a "Send Message" way:
①: Send "verify email" message to "User Registration center";
② send a "verification phone number" message to the "User Registration center";
③ send "Save user" message to "user warehousing";
These three lines of code do not fit to see if you think the message recipients have such ability? Send such a message would someone like to respond? Have you ever thought about how people feel when they receive such a message?
We infer that the "user registry" should have the ability to be: "Registered users", "Logout user", "Such a user can register it?" ”。 The ability to infer that an object should have a lot to do with the name of an object, an object named Usersearchservice should have the ability to "search for a user", and an object named Userprovider should have the ability to "get the user".
When you send a "verify email" message to an object called the "user registry", he will probably not respond to you, which means that the code we are writing is not clearly responsible enough and implies that we need to add an abstraction that responds to this message:
Should send "verify email" message to "email authenticator"-Increase abstract emailvalidator;
The "Verification phone number" message should be sent to the "phone number authenticator"-Add abstract phonevalidator;
Send "Save user" message to "user warehousing"-no problem, user warehousing should have such ability;
After some analysis, the code becomes:
public class Userregistry {private readonly emailvalidator _emailvalidator; Private ReadOnly Phonevalidator _phonevalidator; Private ReadOnly userrepository _userrepository; Public Userregistry (Emailvalidator emailvalidator, Phonevalidator phonevalidator, Userrepository UserRepository) { _emailvalidator = Emailvalidator; _phonevalidator = Phonevalidator; _userrepository = userrepository; public void Register (user user) {if (_emailvalidator.isinvalid. (Email)) throw new Exception (string. Format ("Invalid email:{0}", user. Email)); if (_phonevalidator.isinvalid (user. Phone)) throw new Exception (string. Format ("Invalid Phone number:{0}", user. Phone)); _userrepository.save (user); }} public class Emailvalidator {public bool Isinvalid (string e-mail) {//verify this EM AIL return FALSe }} public class Phonevalidator {public bool Isinvalid (string phone) {//verify this P Hone return false; } }
One of the previous classes became the current 3, each with its own independent responsibilities. In practical applications, it is likely that we need to extend more capabilities to these three classes. During this period, you may want to give phonevalidator more behavior, phonevalidator such a name can not satisfy the demand, you may abstract out the phone class instead of the previous string, The messagesender may be abstracted to send a verification code to the phone number.
A code that has clear responsibilities and a reasonable abstraction is a good explanation for "code as a comment." adding comments to any line of code above is superfluous.
The current code has inadvertently met the OCP (open and close principle), in order to conform to the dip (dependency inversion principle) only need to use the IOC container implementation of injection, and the code is also very good testing.
When you are familiar with the idea of "sending messages", the same ideas can be used on "method calls".
But every time I think I have grasped the object-oriented thought very well, but there will be a new experience in the interval. So the idea described in this article represents only the current stage of my understanding of a single duty, and perhaps after some time I will have a new perspective on a single responsibility.
What do you think of the ideas described in this article?
Write a class with a single responsibility (SRP)