Design Principle Go Design Pattern (3)- The design principle is to design code from a relatively high dimension. No matter how good the design is, the code is not elegant, and the quality of the code is difficult to guarantee.
Even if it was well written at the time, how can you ensure that the code remains elegant as the business changes and other students modify it? Besides, what code is elegant, and who is to judge?
So coding involves at least three things:
What are the criteria for high quality code?
How to write quality code?
How do YOU ensure that your code is always of high quality?
Of course, I am still a student in code writing. If you think it is valuable, you can refer to it. If there is any mistake, I hope you can also criticize and correct it.
Standards for high quality code
Code quality evaluation is highly subjective. The most commonly used criteria for code quality are: maintainability, readability, extensibility, flexibility, simplicity, reusability and testability. Among them, maintainability, readability and extensibility are the most mentioned and the most important three evaluation criteria.
To write high-quality code, you need to use object-oriented design ideas, design principles, design patterns, coding specifications, refactoring techniques, and so on.
Go Design Pattern (3)- Design Principles and Go Design Pattern (2)- Object-oriented analysis and design is more about maintainability, scalability, flexibility, reusability. Readability, conciseness, and testability are related to basic coding ability.
How to write high quality code
2.1 Programming Specifications
Just in terms of writing code that looks elegant, the fastest thing to do is to conform to programming specifications. The programming specification actually has two parts:
- Generic specification, available in all languages
- Specify the language specification, which is written for each language’s own features, such as Go, Java, PHP, etc., there are many online reference
We will cover only general programming specifications here. General programming specifications generally cover three areas: naming and commenting, code style, and programming techniques.
Naming and commenting
Naming and commenting is mainly for readability. Unbug.github. IO /codelf/ unbug.github.
- The key to naming is to be accurate
- Use the information of class to simplify the naming of attribute and function, and use the information of function to simplify the naming of function parameters
- Names should be readable and searchable, and do not use obscure English words that are difficult to pronounce
- The interface is named with the prefix “I”. Suffix “Impl” in implementation class of interface. Abstract classes are named with the prefix “Abstract”. Without a prefix
- Note content: what to do, why, how to do. Complex classes and interfaces, and how to use them.
- Classes and functions should always be commented, and be as comprehensive and detailed as possible
Code style.
- The number of lines of code in a function should not exceed the size of a screen, such as 50 lines
- One line of code should not exceed the DISPLAY width of the IDE
- Use blank lines to split cell blocks
- Two Spaces of indentation are recommended to save space. Make sure you don’t use TAB to indent
- Putting braces on the same line as a statement saves the number of lines of code and creates a new line with a clean structure
- In the GoogleJava programming specification, dependent classes are arranged alphabetically from smallest to largest. In a class, member variables are written before functions, between member variables or functions, static member variables or functions are written first, and ordinary variables or functions are written after, and they are arranged in order of scope size
Programming skills
- Distill complex logic into functions and classes
- Excessive arguments can be handled by splitting them into functions or encapsulating them as objects
- Do not use arguments in functions to control code execution logic
- Function design should have a single responsibility
- Remove too deep nesting levels
- Replace magic numbers with literal constants
- Use explanatory variables to interpret complex expressions
2.2 Code testability
Testability of code is how easy it is for Pointers to write unit tests for code. Easy to write unit tests means that the code is testable, which means that the code is not well designed.
There are two ways to improve the testability of your code
- Dependency injection can be used to mock out uncontrollable dependencies
- Use secondary encapsulation to solve some uncontrollable code behavior
Testability of your code is also a means of checking that your code is of high quality. After you write code, always write unit tests. First you need to think about what test cases you have, and then you start writing unit tests, where you may find that some of the unit tests can’t be written. The reasons are as follows:
- Pending behavior: code and time, random number related. Usually use secondary encapsulation to solve.
- Global variables: Global variables cause the results of a previous run to affect the next run. The reset global variable is used. Let’s see if we can get rid of global variables.
- Static methods: hard to handle and mock if you have to test them. See if you can do without static methods.
- Complex inheritance: A parent class needs to mock out a dependent object for unit testing, so all subclasses and subclasses of subclasses need to mock out that object. If you can do unit testing, it’s a hassle. Let’s see if we can change that to a combination.
- Highly coupled code: Relies on a dozen external objects to get things done and mocks a dozen external objects to unit test. See if you can decouple by refactoring.
How to ensure that the code is always of high quality
There is no alternative to constant refactoring in order for code to remain high quality as the business changes and new people change. It depends. If it’s a new project, everyone is highly skilled, has a sense of constant refactoring, and code Review will also consider code quality issues, so it’s relatively easy to keep code quality high. If it’s an old project with a lot of time and a tight schedule, and code quality is already poor, it’s going to be a lot harder to bring that code up to higher quality.
In both cases, the refactoring scheme is different.
3.1 refactoring
In the first case, optimize using the methods in the programming specification. Of course, new business still needs to use object-oriented design ideas, design principles, design patterns and so on.
In the second case, major refactoring is required. Front-line developers are in the best position to say whether code needs major refactoring. If you don’t know what will happen if a minor change is made to the code, or if you feel that the change will not be able to be made, or if the development of the code seriously affects the project schedule, then it is definitely time to refactor. Of course, reconstruction is a technical work, but there is a premise, we need to work together, a unified understanding, to have the determination of reconstruction.
Large refactorings rely primarily on decoupling. Loose coupling and high cohesion of code are effective means to control code complexity. For decoupling, the following schemes are generally used:
- Encapsulation and abstraction: Effectively hides implementation complexity, isolates implementation variability, and provides a stable and easy-to-use abstract interface to dependent modules
- Introduce the middle tier: Simplify dependencies between modules or classes
- Modularity: Divide and conquer
- Use design ideas and principles: single responsibility, interface based programming rather than implementation, dependency injection, mix more and inheritance less, Demeter development
3.2 Unit Test
Refactoring is risky and requires careful refactoring. Most of the time, people are afraid to do refactoring, mainly for fear that refactoring will cause big problems and affect the business. So how do you make sure refactoring doesn’t cause problems? Theoretically, there is no guarantee that problems will not arise, but there are several ways to avoid risk, the most effective of which is unit testing.
Writing unit tests is not difficult, so some programmers don’t want to do it. In addition, sometimes the project is urgent and there is no time to write unit tests. So the key issue is that the team needs to establish a correct understanding of unit tests. Without unit tests, the code is not allowed to go live, and through various means, the unit tests are written.
There are many benefits to unit testing.
The process of writing unit tests is itself a process of code Review and refactoring, which is an effective way to find bugs and design problems in your code.
In addition, if the unit tests still run during the refactoring process, the quality of the refactoring is ok.
4 instances
With all the theory, you have to look at the code. Just two days ago, my family had to go to the hospital, so I simply wrote a function of snatching numbers. At that time write more urgent, belong to can use the line, while writing this article, take this code to do optimization.
This code is in PHP, because scripts are faster to write (PHP is the best language). It doesn’t really matter what the language is, so let’s optimize the PHP version, and if you’re interested, you can write the Go version.
This function only supports one hospital, I hope that after optimization, it can quickly support the number snatching of multiple hospitals. In addition, the default is to make an appointment through wechat. We’re just using this case for exercise, and hopefully you’re not using it for bad things.
Let’s take a look at what the code looked like before:
$tm = Msectime();
$docName = 'docName';
$patientName = 'Patient name';
$wxID = 'WeChat ID';
$regDay = 'Appointment time';
// Returns the current millisecond timestamp
function Msectime()
{
list($msec.$sec) = explode(' ', microtime());
$msectime = (float) sprintf('%.0f', (floatval($msec) + floatval($sec)) * 1000);
return $msectime;
}
/ / HTTP Get requests
function SendRequest($url)
{
$curl = curl_init();
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_TIMEOUT, 5);
curl_setopt($curl, CURLOPT_URL, $url);
$res = curl_exec($curl);
$resobj = json_decode($res.true);
curl_close($curl);
return $resobj;
}
/ / HTTP Post request
function SendPostRequest($url.$postData)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
$output = curl_exec($ch);
$resobj = json_decode($output.true);
curl_close($ch);
return $resobj;
}
//1. Obtain the personal ID
$uid = -1;
$uInfoUrl = "http://hospital? act=userinfo_oid&uid=$wxID&tm=$tm&oid=$wxID";
$uInfoS = SendRequest($uInfoUrl);
$uInfo = json_decode($uInfoS['data'].true);
$uid = $uInfo['id'];
//2. Obtain the patient id
$sListUrl = "http://hospital? act=member&uid=$uid&oid=$wxID";
$sListS = SendRequest($sListUrl);
$sList = json_decode($sListS['data'].true);
$sickid = -1;
foreach ($sList as $item) {
if ($item['name'] = =$patientName) {
$sickid = $item['id'];
break; }}//3. Get the id of the doctor's available period
$url = "http://hospital/ajax.ashx? act=bespeak_v1&deptid=417&clsid=2&tm=$tm";
$response = SendRequest($url);
$docList = json_decode($response['data'].true);
$bespeakid = -1;
$aorp = 0; //0 am 1 PM
foreach ($docList as $item) {
if ($item['name'] = =$docName && $item['bdate'] = =$regDay) {
if ($item['pm'] != 0 && $item['pm'] != 'about the full') { // There will be an appointment in the afternoon
$aorp = 1;
$bespeakid = (int) ($item['id']);
var_dump($item);
break;
}else if ($item['am'] != 0 && $item['am'] != 'about the full') { // There's a number in the morning
$aorp = 0;
$bespeakid = (int) ($item['id']);
var_dump($item);
break; }}}4 / / registration
if($uid= = -1 || $sickid= = -1 || $bespeakid= = -1){
var_dump($uid.$sickid.$bespeakid.'failed');
exit;
}
$regUrl = "http://hospital? act=bespeak";
$postData = array(
'oid'= >$wxID.'uid'= >$uid.'sickid'= >$sickid.'bespeakid'= >$bespeakid.'aorp'= >$aorp,);$res = SendPostRequest($regUrl.$postData);
if($res['result'] = ='ok'){
var_dump('succcess'.$postData);
}else{
var_dump('failed'.$postData.$res);
}
Copy the code
It can be seen from the code that this function mainly includes the following functions: get the current time, send a request, get the corresponding user ID information of the micro signal, get the patient ID information, get the available time period of the doctor, registration function. And preliminary judgment of the entire use of wechat registration process includes the above steps.
The code has the following problems: use of procedural orientation, naming to be optimized, lack of comments, complex logic not split into functions and classes, repetitive logic, no support for multiple hospitals, request linking and parameter configuration is messy.
We can do the following design: get the current time and put it in the tool class; Send the request to the network class; The operation of interacting with hospitals can be put into one class, but the function of parsing results needs to be replaced. After all, the results returned by each hospital may be inconsistent. Create a factory class to select different hospitals;
The modified code is:
/ / tools
class Utils
{
// Returns the current millisecond timestamp
public function msectime()
{
list($msec.$sec) = explode(' ', microtime());
$msectime = (float) sprintf('%.0f', (floatval($msec) + floatval($sec)) * 1000);
return $msectime;
}
/ / stitching URL
public function buildQuery($url.$arr)
{
return $url."?".http_build_query($arr); }}/ / Http client
class HttpClient
{
/ / HTTP Get requests
function sendGetRequest($url)
{
$curl = curl_init();
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_TIMEOUT, 5);
curl_setopt($curl, CURLOPT_URL, $url);
$output = curl_exec($curl);
curl_close($curl);
return $output;
}
/ / HTTP Post request
function sendPostRequest($url.$postData)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
$output = curl_exec($ch);
curl_close($ch);
return $output; }}// Interact with the hospital system
class HospitalOperation
{
public $httpClient;
public $hospital;
function __construct($hospital)
{
$this->httpClient = new HttpClient();
$this->hospital = $hospital;
}
//1. Obtain the personal ID
public function getUserId()
{
var_dump("Start executing getUserId");
$url = $this->hospital->getUinfoUrl();
$uInfo = $this->httpClient->sendGetRequest($url);
if(empty($uInfo)){
var_dump("Failed to execute getUserId");
return -1;
}
$uid = $this->hospital->getUid($uInfo);
var_dump("GetUserId succeeded and uid is$uid");
return $uid;
}
//2. Obtain the patient id
public function getPatientId()
{
var_dump("Start executing getPatientId");
$url = $this->hospital->getPatientUrl(); var_dump($url);
$listInfo = $this->httpClient->sendGetRequest($url);
if(empty($listInfo)){
var_dump("Failed to execute getPatientId");
return -1;
}
$patientId = $this->hospital->getPatientId($listInfo);
var_dump("GetPatientId succeeded. PatientId is$patientId");
return $patientId;
}
//3. Get the id of the doctor's available period
public function getBresPeakId()
{
var_dump("Start executing getBresPeakId");
$url = $this->hospital->getBresPeakUrl();
$bresInfo = $this->httpClient->sendGetRequest($url);
if(empty($bresInfo)){
var_dump("Failed to execute getBresPeakId");
return -1;
}
$bresPeakId = $this->hospital->getBresPeakId($bresInfo);
if($bresPeakId= = -1){
var_dump("GetBresPeakId failed, no appropriate time");
}else{
var_dump("GetBresPeakId succeeded, bresPeakId is$bresPeakId");
}
return $bresPeakId;
}
4 / / registration
public function registe()
{
var_dump("Start executing registe");
$url = $this->hospital->getRegistUrl();
$postData = $this->hospital->getPostData();
foreach($postData as $checkPoint) {if($checkPoint= = -1){
var_dump('check postdata failed'.$postData);
return -1; }}$regInfo = $this->httpClient->sendPostRequest($url.$postData);
$res = $this->hospital->getRegistRes($regInfo);
if($res= = =true){
var_dump('registe succcess'.$postData);
}else{
var_dump('registe failed'.$postData.$res);
}
return $res;
}
//5. Execute the entire process
function run(){
$res = $this->getUserId();
if($res= = -1) {exit;
}
$res = $this->getPatientId();
if($res= = -1) {exit;
}
$res = $this->getBresPeakId();
if($res= = -1) {exit;
}
$res = $this->registe();
if($res= = -1) {exit; }}}// Return data decode
class Decode
{
public function decodeFunc($data)
{
$data = json_decode($data.true);
return $data; }}/ / class hospital
class Hospital
{
protected $wxId; / / WeChat ID
protected $regDepId;// Make an appointment
protected $docName;// Doctor's name
protected $patientName;// Patient name
protected $regDay;// Date of appointment
protected $uInfoUrl; // Get personal information URL from wechat
protected $patientListUrl; // Get the patient list URL
protected $bresPeakUrl; // Doctor visit time URL
protected $regUrl; / / registered URL
protected $uId; // Wechat ID corresponds to the user ID
protected $postData;// Register the data to be submitted
public $utils;/ / tools
public $decode;// The interface returns data parsing
function __construct($wxId.$regDepId.$docName.$patientName.$regDay.$uInfoUrl.$patientListUrl.$bresPeakUrl.$regUrl.$decode)
{
$this->wxId = $wxId;
$this->regDepId = $regDepId;
$this->docName = $docName;
$this->patientName = $patientName;
$this->regDay = $regDay;
$this->uInfoUrl = $uInfoUrl;
$this->patientListUrl = $patientListUrl;
$this->bresPeakUrl = $bresPeakUrl;
$this->regUrl = $regUrl;
$this->decode = $decode;
$this->utils = new Utils();
$this->initPostData();
}
public function setPostDataItem($key.$value)
{
$this->postData[$key] = $value;
}
public function getPostData()
{
return $this->postData;
}
public function initPostData(){}
public function getUinfoUrl(){}
/** * Obtain the corresponding wechat user ID. -1 indicates that the user id fails to be obtained. */
public function getUid($res) :int {}
public function getPatientUrl(){}
/** * Get the patient Id, -1 means failed to get */
public function getPatientId($res) :int{}
public function getBresPeakUrl(){}
/** * Obtain the doctor's consultation period Id, -1 means failed to obtain */
public function getBresPeakId($res) :int{}
public function getRegistUrl(){}
/** * Obtain the registration result, -1 indicates that the registration failed */
public function getRegistRes($res) :int{}}/** * Class HospitalA * inherits from Hospital Class, mainly used in * 1. Generate the URL of each interface * 2. Parse the returned data to obtain the desired result * 3. Finally generate the registered data postData */
class HospitalA extends Hospital
{
public function initPostData()
{
$this->postData = array(
'oid'= >$this->wxId,
'uid'= > -1.'sickid'= > -1.'bespeakid'= > -1.'aorp'= > -1,); }public function getUinfoUrl()
{
$arr = array(
'act'= >'userinfo_oid'.'uid'= >$this->wxId,
'tm'= >$this->utils->msectime(),
'oid'= >$this->wxId,
);
$url = $this->utils->buildQuery($this->uInfoUrl,$arr);
return $url;
}
public function getUid($res) :int
{
$res = $this->decode->decodeFunc($res);
if($res['result'] != 'ok') {return -1;
}
$uInfo = $this->decode->decodeFunc($res['data']);
$this->setPostDataItem('uid'.$uInfo['id']);
$this->uId = $uInfo['id'];
return $uInfo['id'];
}
public function getPatientUrl()
{
$arr = array(
'act'= >'member'.'uid'= >$this->uId,
'oid'= >$this->wxId,
);
$url = $this->utils->buildQuery($this->patientListUrl,$arr);
return $url;
}
public function getPatientId($res) :int
{
$res = $this->decode->decodeFunc($res);
if($res['result'] != 'ok') {return -1;
}
$list = $this->decode->decodeFunc($res['data']);
foreach ($list as $item) {
if ($item['name'] = =$this->patientName) {
$this->setPostDataItem('sickid'.$item['id']);
return $item['id']; }}return -1;
}
public function getBresPeakUrl()
{
$arr = array(
'act'= >'bespeak_v1'.'deptid'= >$this->regDepId,
'clsid'= >2.'tm'= >$this->utils->msectime(),
);
$url = $this->utils->buildQuery($this->bresPeakUrl,$arr);
return $url;
}
public function getBresPeakId($res) :int
{
$res = $this->decode->decodeFunc($res);
if($res['result'] != 'ok') {return -1;
}
$docList = $this->decode->decodeFunc($res['data']);
$bespeakid = -1;
$aorp = 0; //0 am 1 PM
$flag = 0;
foreach ($docList as $item) {
if ($item['name'] = =$this->docName && $item['bdate'] = =$this->regDay) {
if ($item['pm'] != 0 && $item['pm'] != 'about the full') { // There will be an appointment in the afternoon
$aorp = 1;
$flag = 1;
}else if ($item['am'] != 0 && $item['am'] != 'about the full') { // There's a number in the morning
$aorp = 0;
$flag = 1;
}
if($flag= =1) {$bespeakid = (int) ($item['id']);
var_dump('Choose the doctor as'.$item);
break; }}}$this->setPostDataItem('bespeakid'.$bespeakid);
$this->setPostDataItem('aorp'.$aorp);
return $bespeakid;
}
public function getRegistUrl()
{
$arr = array(
'act'= >'bespeak',);$url = $this->utils->buildQuery($this->regUrl,$arr);
return $url;
}
public function getRegistRes($res) :int
{
$res = $this->decode->decodeFunc($res);
if($res['result'] != 'ok') {return -1;
}
return 1; }}function main(){
$decode = new Decode();
$docName = '* *';
$patientName = '* * *';
$wxId = '* * *';
$regDepId = 0;
$regDay = '2021-03-22';
$uInfoUrl = '* * *';
$patientListUrl = '* * *';
$bresPeakUrl = '* * *';
$regUrl = '* * *';
$hospitalName = 'A';
switch ($hospitalName) {case 'A': $hospital = new HospitalA($wxId.$regDepId.$docName.$patientName.$regDay.$uInfoUrl.$patientListUrl.$bresPeakUrl.$regUrl.$decode);
}
$oper = new HospitalOperation($hospital);
$oper->run();
}
/** * Use: * To connect to a new Hospital, can inherit the Hospital class, implement the function in the class. Make sure that the final postData contains the required data for registration * If the parse is different, or if there is a change in the encoding of the returned data of a hospital, you can inherit the Decode class and implement a new Decode class to replace * For a new hospital, in main, based on the hospital name, Using the factory method to generate the corresponding hospital object * can configure parameters such as URL, which can reduce the incoming data of initialization */
main();
Copy the code
The rewritten code still has some room for optimization, such as error return, function return restriction, parent and subclass relationship restriction, code testability checking, etc. You can try to optimize it a little bit more if you have time.
conclusion
Writing high-quality code takes a lot more effort than just writing it, but with practice, speed and quality improve quickly. It takes awareness, mastery, practice, and success.
The last
If you like my article, you can follow my public account (Programmer Malatang)
My personal blog is shidawuhen.github. IO /
Review of previous articles:
technology
- Go Design Pattern (4)- Code writing
- Go Design Patterns (3)- Design principles
- Go Design Pattern (2)- Object-oriented analysis and design
- Payment access general issues
- HTTP2.0 basics tutorial
- Go Design Pattern (1)- Syntax
- MySQL development specification
- HTTPS Configuration Combat
- Implementation principle of Go channel
- Implementation principle of Go timer
- HTTPS Connection Process
- Current limiting 2
- Seconds kill system
- Distributed systems and consistency protocols
- Service framework and registry for microservices
- Beego framework use
- Discussion on Micro-service
- TCP Performance Optimization
- Current limiting implementation 1
- Redis implements distributed locking
- Golang source BUG tracking
- The implementation principle of atomicity, consistency and persistence of transactions
- CDN request process details
- Common Cache tips
- How to effectively connect with third-party payment
- Gin framework concise version
- InnoDB locks and transactions
- Algorithm is summarized
Reading notes
- The principle of
- History As A Mirror
- Agile revolution
- How to exercise your memory
- Simple Logic – After reading
- Hot Wind – After reading
- Analects of Confucius – After reading
- Sun Tzu’s Art of War – Reflections from reading
thinking
- Anti-liberalism
- practical
- The standard by which you judge yourself
- Service team holiday shift plan
- Project process management
- Some thoughts on project management
- Some thoughts on product manager
- Thinking about programmer career development
- Thinking about code review
- Markdown editor recommends – Typora