Book Image

Refactoring with Microsoft Visual Studio 2010

By : Peter Ritchie
Book Image

Refactoring with Microsoft Visual Studio 2010

By: Peter Ritchie

Overview of this book

<p>Changes to design are an everyday task for many people involved in a software project. Refactoring recognizes this reality and systematizes the distinct process of modifying design and structure without affecting the external behavior of the system. As you consider the benefits of refactoring, you will need this complete guide to steer you through the process of refactoring your code for optimum results.<br /><br />This book will show you how to make your code base more maintainable by detailing various refactorings. Visual Studio includes some basic refactorings that can be used independently or in conjunction to make complex refactorings easier and more approachable. This book will discuss large-scale code management, which typically calls for refactoring. To do this, we will use enterprise editions of Visual Studio, which incorporate features like Application Performance Explorer and Visual Studio Analyzer. These features make it simple to handle code and prove helpful for refactoring quickly.<br /><br />This book introduces you to improving a software system's design through refactoring. It begins with simple refactoring and works its way through complex refactoring. You will learn how to change the design of your software system and how to prioritize refactorings—including how to use various Visual Studio features to focus and prioritize design changes. The book also covers how to ensure quality in the light of seemingly drastic changes to a software system. You will also be able to apply standard established principles and patterns as part of the refactoring effort with the help of this book. You will be able to support your evolving code base by refactoring architectural behavior. As an end result, you will have an adaptable system with improved code readability, maintainability, and navigability.</p>
Table of Contents (17 chapters)
Refactoring with Microsoft Visual Studio 2010
Credits
About the Author
Acknowledgement
About the Reviewers
Preface
6
Improving Class Quality
9
Improving Architectural Behavior

What to refactor


With most software development projects, there's a reasonably constant influx of new requirements or change requests. Depending on the system and the input from your user community, some or many of these requests may require code that is significantly outside what the design currently handles. These requests may be enough to keep your refactoring plate full for a long time.

But, what if your project is already seeing the side-effects of brittle design and hard-to-maintain code? Implementing requirements often introduces bugs in seemingly disparate areas of the code that take less than trivial amounts of time to fix that make estimating the effort to implement requirements less than accurate. Finding where to make code changes to implement requirements may not be obvious, or the changes end up requiring far-reaching changes to code across much of the project. Working on a complex software project often means this can be an everyday fact of life. But, working on a software team where most changes involve modifying code across much of the code base can introduce a friction that makes the time it takes to implement requirements much longer than necessary. Some people may think this is just a part of the software development process; but, because you're reading this book, you don't believe that.

New requirements that clearly don't fit in to the current design offer a clear hint at where to focus our refactoring effort. So, we know that refactoring may produce a more maintainable and robust code base; but, where do we start?

Refactoring to patterns

Many object-oriented code bases are perfectly functional. But these code bases don't consistently attempt to reuse concepts or attempt to use formally accepted patterns. A pattern is a description of communicating objects and classes that are customized to solve a general design problem in a particular context. One way to clean up code is to attempt to refactor individual parts of the code to use specific and applicable patterns. This is an excellent way of better understanding the intention of the code. Once specific patterns are implemented, the code then becomes that much more understandable. Industry-standard terms and code is then strewn throughout the code rather than project-specific buzzwords. The code becomes easier to consume by newcomers and thus easier to maintain. Chapters 5 through 9 deal specifically with examples of code that use concepts found in several common patterns and show how to refactor the code to make the pattern explicit and thus improve the chances of not repeating code.

Just as refactoring to patterns may make code easier to understand, less likely to repeat itself, and easier to maintain in general; forcing patterns into code for the sake of patterns will make code harder to understand and maintain. It's important to be sure that refactoring to a particular pattern adds value to the readability, understandability, and maintainability of the code. Shoe-horning a pattern where it does not belong will have the opposite effect. Make sure your desire to use a pattern hasn't overshadowed its suitability.

Refactoring to principles

In the race to get products out the door or to meet specific deadlines, programmers can often lose sight of principles in favor of functionality. While this isn't all bad, it could leave the code as a procedural ball of mud, or hard to maintain and understand in many other ways. After deadlines have been met, this is often a good time to reflect on the code, its design and structure, as it applies to principles. The code may not be overly object-oriented, for example. The code and the architecture may benefit from a review for SOLID principles. SOLID is an acronym for Single Responsibility principle, Open-Closed principle, Liskov Substitution principle, Interface Segregation principle, and the Dependency Inversion principle. There are several other object-oriented principles geared towards reducing coupling and complexity in source code and help keeping software development efforts focused more on adding value and less on responding to issues. Refactoring to principles is discussed in detail in Chapters 5 through 7.

Refactoring to principles and patterns is a design change. Although changes of this nature have positive side-effects (the impetus to implement them) they may also come with negative side-effects. A design change may decrease performance, for example. Any design change should be evaluated to ensure there are no unacceptable negative side effects before being accepted.

Code smells

Kent Beck introduced the term "code smells" (and formalized it along with Martin Fowler) to provide a way of communicating ways to detect potential problems that can occur in code. These problems are generally adverse side-effects of code that effectively works in at least one context. As with real-world smells, some smells may be tolerable by some people and not by others. There are no good smells with code smells, only varying degrees of bad smells.

Note

Kent Beck and Martin Fowler formalized the term code smells in the book Refactoring: improving the design of existing code.

Code smells allow us to easily define common anti-patterns that can be prioritized by opinion or context. Depending on the person or the project in which they work, a code smell can be distinctly prioritized. Some people and teams may feel certain smells are unacceptable (maybe they consider them as stenches); while others feel the removal of a smell is just a nice-to-have; while still others may feel the same smell is nary an issue.

Code smells are an easily categorized, but personal means of describing the side-effects that someone discovered (and documented) of a particular type of code. So, code smell possibilities and the degree to which you may find them applicable are endless. Chapters 2 and 3 go into more detail about code smells and specific examples of refactoring code in response to common code smells that are generally accepted as having a high return on investment when properly removed.

Complexity

Reducing complexity is always a good reason to change code. Complex code is hard to understand; if code is hard to understand, it's hard for just anyone (or anyone at all) to fix bugs in the code or to add features to the code. But, how do you focus your efforts on complex code and how do you find complex code after all, you want to fix code and not spend all your time searching for code to fix. Fortunately, there are many tools out there that will tell you how complex your code is.

You can get a gut feeling about how complex some code is by simply reading it. But, if you can't read all your code, code metrics can help. Software metrics are numerical measurements calculated by the structure and content of source code. Many software metrics focus on putting a numeric value on the complexity of code. Metrics like Depth of Inheritance, Class Coupling, and Lines of Code can help tell you how complex regions of code are. More obviously, metrics like Maintainability Index and Cyclomatic Complexity specifically address code complexity.

Chapter 6 goes into more detail about specific refactorings in response to specific complexity metrics.

Performance

Focusing on complexity can often indirectly help with the performance of code. But, you often want to focus specifically on performance of code. Fixing performance issues is almost refactoring by definition you want to change the code without changing the external behavior of the code. Many applications have functionally with obvious need of performance improvements; and these are obvious areas of code you should focus upon. But, how do you measure improvement; and if you don't have obvious non-performance features, how do you find areas of code to focus your performance-related refactoring efforts? Luckily, there are many tools out there to gather performance metrics about executed code.

Kernel

Many software systems have a core subsystem that performs the majority of the work that the system does. If a subsystem was designed this way, it's likely called a kernel. Depending on the methodology used by the team, a Domain Model may exist that is very similar to a kernel the code that clearly defines all the domain-specific entities and their logic that remain constant regardless of the type of the front-end, how integration points are implemented, and so on.

Focusing on the core of the system and making sure it's not complex, easy to understand, easy to change, and easy to add features too goes a long way in improving the responsiveness of a software team. This core of code often deals with proprietary information the reason the business exists. People with experience with information and logic like this are usually hard to find. You may not be able to simply publish an ad for "C# developers with experience in Telematics" and expect to find many people locally to fill the position. Keeping the kernel simple to understand means you can get people without years of experience in your domain to change and add to the code.

Design methodologies

It's common for source code that has been around for any length of time to have been worked on by team members that have come and gone. Some of those team members may have been influential with regard to the design methodology used (or there may have been some "rogue" developers that deviated from the design methodology accepted by the rest of the team). With some design methodologies, this may not have a noticeable effect on the design of the code; but some design methodologies have fairly distinct effects on the design of the code. Domain-driven design, for example, suggests that domain entities be explicit in the code usually an entity-to-class relationship. These entities often are completely decoupled from the rest of the system (user-interface, infrastructure, utility methods, and so on.) If the design of the system is to remain domain-driven, you may find that some classes may need to move to a different place, be decoupled from other classes, and so on. Depending on the level to which domain-driven has been implemented (or lacking thereof) the code may need to be more organized into layers. Chapter 8 details refactoring to layers. Other techniques attributed specifically to domain-driven design are detailed in Chapters 8 and 10. Specific patterns have been attributed to domain-driven design and details of those patterns can be seen in the chapters dealing with refactoring to patterns: Chapters 5 through 9.

Unused and highly-used code

Identifying how frequently code is used helps to tell you whether the refactoring effort will result in tangible results. One of the easiest refactorings is to simply delete unused code. But, without scouring the code, line-by-line, how do you find unused code or focus on highly-used code?

Luckily there are tools that will tell you how used code is. These tools are called Code Coverage tools. Much like performance metrics tools, they monitor executing code and tell you how frequently code, methods, and classes are used. Some static analysis tools can tell you about code, methods, and classes that are not referenced by other code giving you information about unused code. This type of unused code will help focus your unused-code refactoring efforts, but can't tell you about all unused code. Code, methods, or classes may still be referenced by other code but may never be executed. Code Coverage tools will help tell you about this other type of unused code.