• Tidak ada hasil yang ditemukan

Chapter 21. High-Quality Programming Code

N/A
N/A
Protected

Academic year: 2024

Membagikan "Chapter 21. High-Quality Programming Code"

Copied!
62
0
0

Teks penuh

The documentation should be at a decent level, or at least the code should be self-documenting. Opposite activities should be named symmetrically (you should be able to guess the name of the opposite activity just by knowing the complementary activity): LoadLibrary() goes with UnloadLibrary() but not with FreeHandle().

Property Names

If the method name is too long (eg more than 50 characters), check that it performs a single task. As with any other code element, parameter naming should be meaningful and contain useful information.

Variable Names

Names of Boolean Identifiers

Named Constants

Naming of Specific Data Types

Naming by Prefixing or Suffixing

Code Formatting

Why Does Code Need Formatting?

Block Formatting

Rules for Formatting a Method

Math.PI * radius;

Rules for Formatting of Types

The opening parenthesis is always on a new line, immediately after the loop or the condition. The body of a loop or conditional statement is always given to the right by a single table.

Usage of Empty Lines

If the condition is long and does not fit on one line, it is transferred to a new line and then shifted to the right by two tabs. You can see that empty lines do not represent the logical structure of the program and therefore violate the main design rule.

Rules for Moving to the Next Line and Alignment

High-Quality Classes

Software Design

Object-Oriented Programming (OOP)

Abstraction

Inheritance

If, in the rare case, this is desired and necessary, the new keyword should be used. A questionable method would be one that re-implements a base method but does nothing more than the corresponding base method. Deep inheritance with more than 6 levels is difficult to trace, debug and maintain and is not recommended.

Encapsulation

Constructors

Deep and Shallow Copy

Notice how changing Iren's age in the original does not affect Iren's age in the copy when we use deep copies.

High-Quality Methods

Why Should We Use Methods?

What Should a Method Do?

Otherwise, the caller loses its ability to handle the error and the cause of the error is lost due to the lack of a sufficiently informative exception. Since only the class author is supposed to call them, he must be aware of the validity of the passed arguments. Therefore, error conditions should not be handled because they can be predicted and prevented in the first place.

Strong Cohesion and Loose Coupling

How Long Should a Method Be?

One of the basic rules for ordering method parameters is that the primary one(s) must precede the rest. This mostly depends on whether the method needs to be aware of the existence of this object or not. Because the final grade depends only on the previous grades and the rest of the student's data doesn't matter, it would be better if only the list of grades is passed – CalcFinalGrade(IList), instead of a Student object .

Proper Use of Variables

An example would be specifying the unit of measurement of a parameter to a method that calculates the cosine of an angle – whether the angle is in radians or degrees, in case the name doesn't make that clear. This way, each of the three parameters will contain some fields inside, and the same information will be passed to the method, but in an easier to understand form. Sometimes it is more appropriate, from a logical point of view, to pass only one or a few of the fields of an object, rather than the entire object.

Returning a Result

It is proven in psychology that the human mind cannot track more than 7 (+/- 2) things at once.

Principles for Initialization

Fortunately, the compiler is smart enough to analyze the control flow and catch such problems - the same error is thrown because not all scenarios assign the variable correctly. If a variable is not initialized when it is declared, but is assigned a value in all possible control flow paths, the compiler will still be satisfied. In order for some objects to be properly initialized, they should have at least some of their fields set.

Declaring a Variable within a Block or a Method

Scope, Lifetime and Span of Variables

The span of a variable corresponds to the average number of lines between occurrences in the code. This improves the quality, readability, understandability, and maintainability of the code because less code needs to be inspected to read and understand the code. This maximizes the portion of the code that you can safely ignore when reading the code.

It is important that the programmer tracks the use of a particular variable along with its scope, span, and lifetime. The main objective is to reduce the scope, lifetime and span as much as possible. Declare local variables as late as possible, immediately before using them for the first time.

Use of Variables – More Rules

Variables with wider scopes and longer lifetimes should have more descriptive names, such as totalStudentsCount instead of count. For example, John left the variable X for Tom to see so that he could come to a conclusion to implement another method that would use the same variable.

Proper Use of Expressions

In the event of an error, it would be much more difficult to debug a complex expression, because it remains on one line, and debuggers step through the code in terms of lines. This way the code becomes simpler, easier to read and understand and easier to maintain, debug and repair. Only one operation should be performed on one line, otherwise the code becomes difficult to read, maintain, debug, and modify.

Use of Constants

All these reasons suggest that writing complex expressions is harmful and should be avoided. Without knowing the exact calculation this code is supposed to do, it's still hard to understand, but at least we can debug it on the exception and find which line is causing it, and eventually fix it. It occurs to me that it is better to define repeating values ​​only once in the code.

When to Use Constants?

When Not to Use Constants?

SQL queries in named constants are not recommended (in case you are using a database, queries are usually written in SQL and it is usually a string in programming language terms). Button labels, dialog box titles, menu items, and captions for other UI components should not be declared as named constants. Use named constants to avoid using and duplicating magic numbers and strings, and mostly to improve code readability.

Proper Use of Control Flow Statements

This approach is encouraged if the program you are writing will need to be internationalized. If introducing a named constant hinders readability, better leave the hardcoded value in the code.

With or Without Curly Brackets?

This is because an if statement without curly braces only takes the first statement as its body, regardless of indentation, which makes things confusing.

Proper Usage of Conditional Statements

To improve this, we can introduce some more methods where parts of the logic are executed and isolated. Extracting parts of the code into separate methods is the easiest and most efficient way to reduce the level of nesting of a group of conditional statements, while preserving their logic.

Proper Use of Loops

If it is necessary to check certain conditions to stop the execution of the loop, then it is probably better to choose a while loop. A while loop is suitable in cases where the exact number of iterations is not known. If the prerequisites for using a while loop are in place, but the loop body must execute unconditionally at least once, a do-while loop should be used instead.

Defensive Programming

Deep nesting usually happens due to a large number of loops and conditional statements occurring within each other.

Assertions

Assertions vs. Exceptions

Defensive Programming with Exceptions

Exceptions should be used to inform other parts of the code of problems that should not be ignored. If a particular problem can be treated locally, the treatment should be carried out in the method itself and no exceptions should be made.

Code Documentation

Self-Documenting Code

Properties of Self-Documenting Code

Self-Documenting Code – Important Questions

Effective Comments

If, instead of writing naive comments, we write comments to clarify invisible facts in the code, comments can be very useful. In such a case, it is enough just to describe what is the purpose of the particular method and its general idea, in a single sentence. To write effective comments, it is desirable to use pseudo-code whenever possible.

XML Documentation in C#

The XML documentation is automatically processed by Visual Studio and displayed in its auto-completion feature. XML documentation can be translated into an MSDN-style website or e-book (in CHM format) using specialized tools such as Sandcastle (http://shfb.codeplex.com). More information on writing and using XML documentation can be found in the MSDN library: http://msdn.microsoft.com/en-us/library/b2s063f7.aspx.

Code Refactoring

Refactoring at Data Level

Refactoring at Method and Class Level

Unit Testing

Unit Testing – Example

The Sum(…) method seems to work correctly in the typical case: the sum of 1+2 is 3 (as expected) and the above code produces nothing. Instead, we could start summing from l from the first element in the array (which may be missing when an empty array is passed as an argument). Now we can be sure that the Sum(…) method works correctly (even in unusual situations) and is well tested.

Benefits of Unit Testing

The above unit test is a bit more complicated: it expects an exception and if it is not thrown, it fails. For example, if you download the source code of Firefox, you will notice that half of the code is written to run unit tests on the other half of the code. In practice, it is impossible to write complex product (like for example MS Word or Android OS or Firefox browser) without unit testing.

Benefits of Unit Testing – Example

It may happen that we refactor the code to improve its internal quality but accidentally after refactoring the code does not work correctly in all special cases. The conclusion from the above experience is that when we modify the code and the unit test fails, either the tested code is incorrect, or the unit test is incorrect. When we develop a complex software product, we want the features that work in the current version to continue to work the same way in all of its future versions.

Unit Testing Frameworks and Tools

For example, if we work in MS Word and add PDF export for its next version, we want to be sure that saving in DOCX format still works even after introducing PDF export.

Unit Testing with Visual Studio Team Test (VSTT)

So if you are using Visual Studio 2012, you will need to manually create a unit test project (File  New Project  Unit Test Project). A detailed explanation of VSTT will not be provided in this book, but anyone could explore how to use unit testing in Visual Studio. VSTT can test private methods, set a time limit on test execution, and expect exceptions to be thrown by certain test cases – things that simplify writing the test code.

Additional Resources

Exercises

When no continuation is available in the current direction (either matrix wall or non-empty cell is reached), the direction is changed to the next possible clockwise direction. When no empty cell is available in all directions, the walk restarts from an empty cell in the smallest possible row and as close to the beginning of this row as possible. Note that debugging the solution may be necessary if it is not working properly.

Solutions and Guidelines

Your task is to write a program that reads an integer n (1 ≤ n ≤ млл) from the console and displays the filled matrix on the console. Refactor the code so that it meets the recommended standards for code quality set in this chapter.

Referensi

Dokumen terkait