CHAPTER 2

image

Software Craftsmanship

“The proof is in the pudding!”

Software as an Art and a Science

Now that we’ve covered the Extensible Application Markup Language (XAML) and its different “dialects,” or anomalies that exist between WPF, Windows 8.1/Windows Store, and Windows Phone 8.1 XAML, we can concentrate on the design patterns and industry best practices that you’ll use throughout the rest of this book to design and develop a “smart client” line-of-business (LOB) application. The primary idea of this book is to take you through the experience of working with a virtual (fictional) team. Doing so will aid you in learning the key concepts involved by presenting some of the common issues that you will face and providing you with tips that will resonate with you no matter what area of IT you work in.

The patterns and paradigms presented in this book will provide you with the information you’ll need to design and develop robust, reusable, business domain–related software solutions that solve problems found in the enterprise while using effective design methodologies, design patterns, and other tools that are used throughout the software development community.

The book will offer tips and tricks on how to achieve the highest level of user interface layer component reuse between the three XAML-based target platforms. The applications in this book are designed with flexibility in mind. The book will concentrate on the SOLID object-oriented design principles to aid you when designing the classes that make up your applications. The ultimate goal is to illustrate how to design applications that are divided into logical layers that promote a clear separation of concerns. Physically, the layers will represents class libraries that are concise, flexible, extensible, and loosely coupled from other components throughout the system. This will maximize code reuse throughout your organization, as well as make your code easier to write unit tests for.

SOLID Object-Oriented Design

The S in SOLID stands for the Single Responsibility principle. This principle means that a class should have one and only one responsibility. As an example, your Order class shouldn’t be concerned with how yearly reports are printed. This would be the responsibility of a class called ReportPrintService. The Order class should be responsible only for describing an order in the system.

The O in SOLID stands for the Open/Close principle. This means you should design your code to be open to extensibility and closed to modification. You may ask yourself, “How can I make a class open to being extended without modifying the code?” There are several ways to assist you with this principle. For instance, you could create a base class and inherit from the base class to implement methods on your own. Our favorite approach is a design pattern called the Decorator pattern. You can find the official definition at http://sourcemaking.com/design_patterns/decorator.

“The Decorator Pattern attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclass for extending functionality.”

We will provide a full example of this pattern, as well as other useful design patterns, later in this chapter. The L in SOLID stands for the Liskov substitution principle. This means you should be able to reference an instance of your class as any base class or any interface in which the class implements. The I in SOLID stands for the Interface Segregation principle, which states that when possible, you should code to an interface rather than a concrete implementation. Finally, the D in SOLID stands for the Dependency Inversion principle, which promotes two patterns: Dependency Injection (DI) and Inversion of Control.

Image Note  The SOLID object design principles represent an acronym in which each letter represents a principle to consider when designing a class, property, or method in object-oriented design.

To Unit Test or Not to Unit Test?

The topic of unit testing spurs debates where each participant feels strongly about their stance. One party might say, “Unit tests sound great in theory, but I don’t have time to meet deadlines and write unit tests from which I gain nothing!” Another individual may insist, “Unit tests are the best thing since sliced bread and the butter that you put on it!” A third party may make the claim, “I’ve written code for more than 20 years just fine without unit tests.” Finally, there may be an individual who believes, “With unit tests, I know the moment that a bug is introduced into the system, as well as where to find it.” These are just a few of the arguments that come to mind.

There is no absolute “correct” answer. Honestly, does unit testing add extra development time? Generally, the answer is “yes.” Does unit testing help reduce the bugs in an application? There have been many studies that suggest that unit testing does in fact help make your code less error prone. Just like anything else, unit testing takes practice to gain proficiency, which means that your unit tests will provide quality protection only when they are quality tests. You have to know what to test and what unit tests are redundant or misleading. Practice makes perfect!

Test-Driven Development

While we are on the topic of unit tests, we’re obligated to talk a bit about Test-Driven Development (TDD). TDD is a software development paradigm in which you write unit tests before you even implement the class under testing. If you write each test before the method you are testing, you guarantee that all methods will be covered with a unit test.

There is another benefit to using TDD that many people overlook. If you write a unit test for the method PrintService.PrintReport() before a PrintService.PrintReport() exists, then you are designing the method and the public contract of the PrintService class. This design is based on how you would naturally imagine the class should be used. Thus, not only do you guarantee that each method has at least one unit test associated with it, you also get to design your classes based on how you would use the class. When the unit test is finished, it can serve as a succinct example of how the class should be used.

There is more to TDD than writing unit tests first, so here are the rules for TDD:

  • The first rule to consider is “Make sure that the unit test fails.” Obviously, if you write the test before the class under testing even exists, your solution will not compile. This isn’t the same as the first failure you are after, so you’ll want to create an empty class declaration for the classes used in the test so that it will build properly. Once the test builds, then run it to experience the first failure. Once your test fails, the next step is to make the smallest change possible to the class under test and then run it again. Do this until the test passes. The key to this process is the importance of making small incremental changes until the test passes. If you were to get your unit test to build and fail the first time, then you would move forward to make the test pass. We recommend that you then write the entire method quickly, trying to anticipate issues and other ideas for the class that you would then implement. Instead of making small incremental changes to achieve a passing unit test, make one big change and even add some fields or methods that you know will be coming up. So, what’s the big deal? Well, when you make the smallest changes possible in an incremental fashion, you will add only what is absolutely necessary, which will greatly reduce the size and complexity of your code.
  • Now it’s time to refactor the method. Since you used small, incremental changes, you should refactor the method that you’ve created to make sure that it is clean and concise. Thus, the cycle goes like this: Red image Green image Refactor image Retest.
    • Red: The unit test failed because of no implementation.
    • Green: The unit test passes after small, incremental changes. The small incremental changes guarantee that you don’t jump ahead of yourself and that you add only the code that is needed to make the test pass, and this can reduce complexity.
    • Refactor: Go back and refactor your new code to clean up the implementation, and test again to make sure your refactoring didn’t break anything.
    • Retest: Move on to your next test.

Imagine that you wanted to write an application that would accept a user’s name during login and return a welcome message to the user. A TDD style would go something like this:

  1. Create a new blank solution in Visual Studio.
  2. Create a class library for the functionality of the program, create a new unit test project for the solution, and add a reference to the class library so that the test can actually use the library to test it.
  3. Rename the default unit test class that is created with the unit test project.
  4. Create your first test, which would looking something like the one shown in Listing 2-1.

As you can see, there is a simple test class here. You create an IUserLoginServer interface and a UserLoginService class in the class library so that the solution will compile. Nevertheless, there is no implementation, so the test will fail at first (which is what you want to happen).

Now that the test has successfully failed, it’s time to make small incremental changes until the test passes, as shown in Listing 2-2.

As you can see in Listing 2-3, the only thing that exists in the class is the GreetUser method declaration. This is required for the solution to build so that the test can fail. Now, you need to make the smallest change possible to make the test pass. This should be pretty easy.

Replace the line in which the NonImplementedException is thrown with the functionality that is expected in the unit test. Listing 2-4 shows the class with this small change added.

As you can see, only one line of code is different, and when the test is run again, it passes. However, look at the code for a moment. There are no null reference checks on the validName parameter. This validName parameter should have a name that fits better with its purpose, and you should always check for null values.

This is the part of TDD that was hard for us to get used to. Starting out, we would have identified these issues and resolved them on the first edit of the class. Through much practice, however, we have learned to wait until the refactor stage of the process so that we can reap the benefits of changing only the minimum amount of code on each iteration until the test passes.

Now that it’s time to refactor the method, make the modifications shown in Listing 2-5, and retest until all tests pass. Then move on to the next test and, in return, the next feature.

As you can see, you’ve added some defensive programming (null reference checks), renamed the parameter, and changed the method’s return type. Once you’ve finished these refactorings, the test is rerun, and this time it passes. That’s how we prefer to use TDD. You will write many more unit tests in this book before the solution is complete.

Meet the Team

Design methodologies and design patterns are the major components of any software project, but before we get into the design patterns that we will use in this book, let’s meet our virtual team! The scenario is that you’ve recently accepted an offer from Acme Systems to assist in the development of several line-of-business applications. The company has recently changed owners, and they’ve decided to bring all enterprise systems development in-house. The team is filled with individuals who have their own roles to play, have their own opinions, and bring different levels of experience to their jobs.

The Development Manager

The development manager is the person to whom you report. The development manager’s job is to manage all of the resources allocated for this new company initiative. The development manager must stay on top of people, project budgets, and timelines.

The Business Analyst

The business analyst knows the business. Even more important, the business analyst knows what questions to ask the business users in order to get useful information regarding the software project. The business analyst will take requirements-gathering questions from the developers, provide explanations on why they are problematic, and offer solutions. The business analyst’s role is to emphasize the importance of gaining knowledge of the business domain to ensure that the users’ needs are met.

The Junior Developer

The junior developer is an entry-level .NET programmer with the least amount of experience on the team. The junior developer is fresh out of college. This is the developer’s first job in software development. Everyone says that the junior developer is a quick learner who loves doing software development. The junior developer is enthusiastic, which can sometimes be misinterpreted as being overzealous—but this individual means well. The junior developer stays on top of all of the newest technologies and is quick to suggest their use on projects. The junior developer will look to you for guidance, and in pointing out mistakes, you can learn something new too. Most people can relate to the junior developer; after all, everyone was that person once in their careers.

The Guru

The guru works magic with the keyboard. The guru’s experience includes all of the design patterns and technologies used throughout the book. The guru is the “go-to” guy (yes, pun intended) for anything related to software (and then some). The guru can be stubborn when it comes to software development, though this individual has been known to adopt a new technology after personally considering all of the important factors. The guru is a bit of an introvert; however, when asked, this individual is happy to provide guidance to the team.

The DBA

The DBA works in the realm of data access and storage. The DBA holds the keys to the database (yes, pun intended again). The DBA is in control of data access–related decisions from database server types used to the schemas, roles, users, and permissions. The DBA is in charge of schema change requests during development. The DBA is also responsible for database backup and restoration (disaster recovery). You must always remember that the DBA is not only in charge of new systems but also of old legacy systems.

The First Team Design Meeting

The first design meeting consists of you, the junior developer, the guru, the business analyst, the DBA, the product champion, and the development manager.

During this meeting, the team’s agenda is as follows:

  • Have the team meet and get acquainted with one another
  • Discuss the business problem that will be solved by the team
  • Identify each person’s role on the team
  • Discuss any project deadlines that exist
  • Decide on a team development paradigm
  • Create meeting schedules
  • Decide on the methods of communication that the team will use
  • Decide on technologies that will be used (based on the information that’s been given)

In the first meeting, you will learn about the business problem that needs to be solved. Aside from the product champion, the business analyst has the greatest knowledge of the business’s processes on the team. The product champion is an employee of the client and is considered a “power user.” These individuals have been chosen to allocate time every week to assist with the development of the solution.

As the meeting starts, the business analyst explains the details of the business problem to be solved. “As many of you know, Acme has decided to bring all system development in-house. This decision came about when it was discovered that a mandatory accounting system update broke integration with the in-house purchasing requisition system used throughout Acme Systems.”

The plan is to rewrite the entire accounting system over the course of one year. The decision to start with the purchasing requisition system was made because the old solution no longer works with the legacy system.

If there is anything that we’ve learned over the last ten years in software development, it is this: if you provide a software developer with 20 reams of printed UML diagrams and other legacy-style design artifacts and you request that they create a software solution, nine times out of ten you will end up with an application that makes perfect sense to the developer and yet still does not click with business users’ ideas of what the system should be. This is because typical software developers are experts in software development, not in the business domain in which they are solving a business problem. Developers don’t think like users.

The point is that you need feedback from a user of the system, and you need the feedback often during the development of the software solution. With constant feedback during development, you can rest assured that you are developing for the users’ needs.

Scrum Agile Methodologies

All team members have experience with the Scrum agile team paradigm, so they decide to use Scrum and agile methodologies. As such, it’s decided that the team will work in two-week sprints, or iterations, of development and that they will use Team Foundation Services (TFS) 2013 to record the product backlog, plan and schedule sprints, and create user stories and tasks, as well as for source control for the application.

TFS is a great choice because of its awesome integration with Visual Studio. It’s a one-stop shop for tracking issues, tracking bugs, recording user stories, and providing estimates. You can create a user story through its life cycle, from creation through creating specific child tasks that contain all of the information you need to develop the story, all the way through acceptance testing.

The integration of TFS with SharePoint adds a nice web-friendly user interface. You can create Excel spreadsheets that display graphs relating to sprints completed and the remainder of your product backlog. The SharePoint TFS portal has a wiki that the team can use to capture important information regarding the project. You can plan sprints from the Web and much more.

One of the features of TFS is an absolute godsend—its integration with Microsoft Office. You can create queries to retrieve a subset of TFS work items and export the list to an Excel spreadsheet. The spreadsheet will allow you to work on batch item creation with little effort as compared to entering 50-plus user stories manually. Furthermore, some people will demand a spreadsheet over a URL to all of the information they could possibly need. Ah, the communal spreadsheet—one spreadsheet, one network share, and four different users. You may have been there are smiling and gritting your teeth. Those of you who haven’t been there, be thankful.

The team decides that the software will be created with as much unit test coverage as possible, and they will use the mstest functionality built into Visual Studio to maintain the unit tests. The team requests another meeting in two weeks to work with the product champion to create user stories and identify other requirements.

Image Note  TFS is Microsoft’s source control solution. It provides work item tracking, automated builds, and much more.

How to “Talk the Talk” When It Comes to Gathering Requirements

As a developer, I’m sure you’ve realized that users of your business applications line don’t care that you implemented a linked list from scratch while writing the program. They care about how the system is used, the results it produces, and the speed at which the results are produced. Thus, it’s important for developers to work closely with business users to learn as much as possible about the business side of a user’s day-to-day workload and how to create software to improve their jobs.

One major component to doing this is simply vocabulary. During brainstorming sessions, be sure to identify nouns and verbs as the business analyst and end user explain the system. If you ask a question and the answer starts with something like “Do you mean a ______?” these are the terms that you need to identify and record so that they can later become part of your ubiquitous language (see Chapter 3).

The more you know about the business side of the problem for which you are creating a solution, the better your chances are of success. Try to study the business domain in nonprogramming terms. Perhaps offer to buy the business analyst or the business user lunch so that you can discuss any questions you may have. Try to speak their language.

User Stories and How to Create Them

In agile development, a user story is basically a simplified use case. It represents an interaction between the user and the system. Sometimes, a user story will even represent an interaction between two systems!

The Anatomy of a Good User Story

There are many well-known traits of a good user story. First, a user story must serve a value to the business. This means the user story should be written using the ubiquitous language of the business domain so that anyone involved can understand the request.

Another key to a great user story is brevity. The idea isn’t to hash out every aspect of the design and development of a user story’s features. Rather, you should capture just enough information to be able to refer to the user story later on during development, and if the feature makes the cut, then use the user story to start planning the design of the feature. Had you created the user story with the feature’s design choice at the start and the user story didn’t make the cut, you would have lost all of the time working on a feature’s design that would not be implemented. Keep it short, sweet, and in the terms of the business domain.

When consulting, we use index cards when we’re creating user stories. You can even ask the business user if they wouldn’t mind trying to write the stories on these cards in their own words. If the user explains the user story, you definitely capture what the user wants and in the business domain’s terms. If you’re writing them yourself, you can write a story title, the date, and then a brief description. Remember, the idea is not to capture all of the important details; the point is to write enough to remember how to ask for the details when they are needed.

Summary

Indeed, software development is both an art and a science. Over the last ten years, there have been major advancements in programming styles, software design, and requirements gathering. Somewhere along the way, we learned to stop creating massive amounts of work at the beginning of a project. Instead, we began to understand that change is nearly guaranteed, and the current style of design was neither conducive nor friendly to change. These changes ruined estimates and then budgets and ultimately produced software that the user had to learn to “deal with” rather than enjoy.

Now we have agile methodologies that put the business first. Without a business problem, there is no software solution to build. We’ve learned that user involvement in design is vital to a business application line and that it’s best to be involved with a selected user to show progress and to receive feedback immediately.

There’s another software design style that lines right up with our newfound views of a business-centric software development solution. It’s called Domain-Driven Design (DDD), and we will discuss this design methodology in the next chapter.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.223.170.63