12

Implementing Code Reusability in C# 10

Code reusability is one of the most important topics in software architecture. This chapter aims to discuss ways to enable code reuse, as well as to help you understand how .NET 6 solves the problem of managing and maintaining a reusable library.

The following topics will be covered in this chapter:

  • Understanding the principles of code reuse
  • The advantages of working with .NET 6
  • Creating reusable libraries using .NET 6

Although code reuse is an exceptional practice, as a software architect you must be aware when this is important for the scenario you are dealing with. Many good software architects agree that there is a lot of overengineering due to trying to make things reusable even though they are often single-use or not understood well enough.

Technical requirements

This chapter requires the following things:

Understanding the principles of code reusability

There is a single reason that you can always use to justify code reuse – you cannot spend your valuable time reinventing the wheel if it is already running well in other scenarios. That is why most engineering domains are based on reusability principles. Think about the light switches you have in your house.

Can you imagine the number of applications that can be made with the same interface components? The fundamentals of code reuse are the same. Again, it is a matter of planning a good solution so part of it can be reused later.

In software engineering, code reuse is one of the techniques that can bring a software project a bunch of advantages, such as the following:

  • There is confidence in the software, considering that the reused piece of code was already tested in another application.
  • There is better usage of software architects and the senior team since they can be dedicated to solving this kind of problem.
  • There is the possibility of bringing to the project a pattern that is already accepted by the market.
  • Development speed goes up due to the already implemented components.
  • Maintenance is easier.

These aspects indicate that code reuse should be done whenever it is possible. It is your responsibility, as a software architect, to ensure the preceding advantages are utilized and, more than that, that you incentivize your team to enable reuse in the software they are creating.

What is not code reuse?

The first thing you must understand is that code reuse does not mean copying and pasting code from one class to another. Even if this code was written by another team or project, this does not indicate that you are properly working with reusability principles. Let us imagine a scenario that we will find in this book’s use case, the WWTravelClub evaluation.

In this project scenario, you may want to evaluate different kinds of subjects, such as the Package, DestinationExpert, City, Comments, and so on. The process for getting the evaluation average is the same, no matter which subject you are referring to. Due to this, you may want to enable reuse by copying and pasting the code for each evaluation. The (bad) result will be something like this:

Diagram  Description automatically generated

Figure 12.1: Bad implementation – there is no code reuse here

In the preceding diagram, the process of calculating the evaluation average is decentralized, which means that the same code will be duplicated in different classes. This will cause a lot of trouble, especially if the same approach is used in other applications. For instance, if there is a new specification about how you must calculate the average or if you just get a bug in the calculation formula, you will have to fix it in all instances of code. If you do not remember to update it in all places, you will possibly end up with an inconsistent implementation.

What is code reuse?

The solution to the problem mentioned in the previous section is quite simple: you must analyze your code and select the parts of it that would be good to decouple from your application.

The greatest reason why you should decouple them is related to how you are sure that this code can be reused in other parts of the application, or even in another application:

Diagram  Description automatically generated

Figure 12.2: An implementation focused on code reuse

The centralization of the code brings with it a different responsibility for software architects, such as yourself. You will have to keep in mind that a bug or incompatibility in this code could cause damage to many parts of the application or different applications. On the other hand, once you have this code tested and running, you will be able to propagate its usage with no worries. Besides, if you need to evolve the average calculation process, you will have to change the code in a single class.

It is worth mentioning that the more you use the same code, the cheaper this development will become. Cost needs to be mentioned because, in general, the conception of reusable software costs more in the beginning.

Reusability in the development life cycle

If you understood that reusability will take you to another level of code implementation, you should have been thinking about how to make this technique available in your development life cycle.

As a matter of fact, creating and maintaining a component library is not very easy, due to the responsibility you will have and the lack of good tools to support the search for existing components.

On the other hand, there are some things that you may consider implementing in your software development process every time you initiate a new development:

  • Use already implemented components from your user library, selecting features in the software requirements specification that need them.
  • Identify features in the software requirements specification that are candidates to be designed as library components.
  • Modify the specification, considering that these features will be developed using reusable components.
  • Design the reusable components and be sure that they have the appropriate interfaces to be used in many projects.
  • Build the project architecture with the new component library version.
  • Document the component library version so that every developer and team knows about it.

The use-identify-modify-design-build process is a technique that you may consider implementing every time you need to enable software reuse. As soon as you have the components you need to write for this library, you will need to decide on the technology that will provide these components.

During the history of software development, there have been many approaches to enable code reuse. From dynamic link libraries (DLLs) to microservices – as we discussed in Chapter 5, Applying a Microservice Architecture to Your Enterprise Application, in the Microservices and the evolution of the concept of modules section. The methodology explained in the section can be used by you, as a software architect, to implement this strategy to accelerate software development. Now let us check how .NET 6 can help us with it.

Using .NET 6 for code reuse

.NET has evolved a lot since its first version. This evolution is not only related to the number of commands and performance issues but the supported platforms too. As we discussed in Chapter 1, Understanding the Importance of Software Architecture, you can run C# .NET on billions of devices, even if they are running Linux, Android, macOS, or iOS. For this reason, .NET Standard was first announced together with .NET Core 1.0, but .NET Standard became particularly important with .NET Standard 2.0, when .NET Framework 4.7.2, .NET Core, and Xamarin were compatible with it.

The key point is that .NET Standard was not only a kind of Visual Studio project. More than that, it was a formal specification available to all .NET implementations. As you can see in the following table, it covers everything from .NET Framework to Unity:

.NET Standard

1.0

1.1

1.2

1.3

1.4

1.5

1.6

2.0

2.1

.NET Core and .NET 5

1.0

1.0

1.0

1.0

1.0

1.0

1.0

2.0

3.0

.NET Framework

4.5

4.5

4.5.1

4.6

4.6.1

4.6.1

4.6.1

4.6.1

N/A

You can find a full .NET Standard overview at https://docs.microsoft.com/en-us/dotnet/standard/net-standard.

The preceding table indicates that if you build a class library that is compatible with this standard, you will be able to reuse it in any of the platforms presented. Think about how fast your development process could become if you plan to do this in all your projects.

Obviously, some components are not included in .NET Standard, but its evolution is continuous. It is worth mentioning that Microsoft’s official documentation indicates that the higher the version, the more APIs are available to you.

The initiative of having a single framework for all platforms brought us to .NET 5. Microsoft indicated that from .NET 5.0, the framework would run everywhere. The next question you, as a software architect, might have is: what is going to happen to .NET Standard?

The answer to this question is well explained by Immo Landwerth at the dotnet blog: https://devblogs.microsoft.com/dotnet/the-future-of-net-standard/. The basic answer is that .NET 5.0 (and future versions) needs to be thought of as the foundation for sharing code moving forward. Considering .NET 6 is an LTS version, now we can understand the framework as the best option to share code for new applications.

Creating a reusable class library

It is quite simple to create a class library. Basically, you need to choose the following project when creating the library:

Texto  Descrição gerada automaticamente

Figure 12.3: Creating a class library

Once you have concluded this part, you will notice that the project file keeps the information about the target framework moniker (TFM). The idea of the TFM is to define the set of APIs that will be available to the library. You can find the list of the available TFMs at https://docs.microsoft.com/en-us/dotnet/standard/frameworks.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
  </PropertyGroup>
</Project>

As soon as your project is loaded, you can start coding the classes that you intend to reuse. The advantage of building reusable classes using this approach is that you will be able to reuse the written code in all the project types we checked previously. On the other hand, you will find out that some APIs that are available in .NET Framework do not exist in this type of project.

How does C# deal with code reuse?

There are many approaches where C# helps us deal with code reuse. The ability to build libraries, as we did in the previous section, is one of them. One of the most important ones is the fact that the language is object-oriented. Besides, it is worth mentioning the facilities that generics brought to the C# language. This section will discuss the last two we mentioned.

Object-oriented analysis

The object-oriented analysis approach gives us the ability to reuse code in different ways, from the facility of inheritance to the changeability of polymorphism. Complete adoption of object-oriented programming will let you implement abstraction and encapsulation too.

It is important to mention that in Chapter 20, Best Practices in Coding C# 10, we discuss how inheritance can cause complexity in your code. Although the example below presents a valid way to reuse code, consider using composition over inheritance in real-life applications.

The following diagram shows how using the object-oriented approach makes reuse easier. As you can see, there are different ways to calculate the grades of an evaluation, considering you can be a basic or a prime user of the system:

Diagrama  Descrição gerada automaticamente

Figure 12.4: Object-oriented case analysis

There are two aspects to be analyzed as code reuse in this design. The first is that there is no need to declare the properties in each child class since inheritance is doing it for you.

The second is the opportunity to use polymorphism, enabling different behaviors for the same method:

public class PrimeUsersEvaluation : Evaluation
{
    /// <summary>
    /// The business rule implemented here indicates that grades that 
    /// came from prime users have 20% of increase
    /// </summary>
    /// <returns>the final grade from a prime user</returns>
    public override double CalculateGrade()
    {
         return Grade * 1.2;
    }
}

In the preceding code, you can see the usage of the polymorphism principle, where the calculation of evaluation for prime users will increase by 20%. Now, look at how easy it is to call different objects inherited by the same class. Since the collection content implements the same interface, IContentEvaluated, it can have basic and prime users too:

public class EvaluationService
{
    public IContentEvaluated Content { get; set; }
    /// <summary>
    /// No matter the Evaluation, the calculation will always get
    /// values from the method CalculateGrade
    /// </summary>
    /// <returns>The average of the grade from Evaluations</returns>
    public double CalculateEvaluationAverage()
    {
            return Content.Evaluations
                .Select(x => x.CalculateGrade())
                .Average();
    }
}

Object-oriented adoption can be considered mandatory when using C#. However, more specific usage will need study and practice. You, as a software architect, should always incentivize your team to study object-oriented analysis. The more abstraction abilities they have, the easier code reuse will become.

Generics

Generics were introduced in C# in version 2.0, and it is considered an approach that increases code reuse. It also maximizes type safety and performance.

The basic principle of generics is that you can define in an interface, class, method, property, event, or even a delegate, a placeholder that will be replaced with a specific type later when one of the preceding entities is used. The opportunity you have with this feature is incredible since you can use the same code to run different versions of the type, generically.

The following code is a modification of EvaluationService, which was presented in the previous section. The idea here is to enable the generalization of the service, giving us the opportunity to define the goal of evaluation since its creation:

public class EvaluationService<T> where T: IContentEvaluated, new()

This declaration indicates that any class that implements the IContentEvaluated interface can be used for this service. The new constraint indicates this class must have a public parameter-less default constructor.

Besides, the service will be responsible for creating the evaluated content.

public EvaluationService()
{
    var name = GetTypeOfEvaluation();
    content = new T();
}

It is worth mentioning that this code will work because all the classes are in the same assembly. The result of this modification can be checked in the instance creation of the service:

var service = new EvaluationService<CityEvaluation>();

The good news is that, now, you have a generic service that will automatically instantiate the list object with the evaluations of the content you need. It’s worth mentioning that generics will obviously need more time dedicated to the first project’s construction. However, once the design is done, you will have good, fast, and easy-to-maintain code. This is what we call reuse!

What if the code is not reusable?

In fact, any code can be reusable. The key point here is if the code you intend to reuse is well-written and follows good patterns for reuse. There are several reasons why code should be considered not ready for reuse:

  • The code was not tested before: Before reusing code, it is a good approach to guarantee that it works.
  • The code is duplicated: If you have duplicate code, you will need to find each place where it is being used so you only have a single version of the code being reused.
  • The code is too complex to understand: Code that is reused in many places needs to be written with simplicity to enable easy understanding.
  • The code has tight coupling: This is a discussion related to composition versus inheritance when building separate class libraries. Classes (with interfaces) are usually much easier to reuse than base classes that can be inherited.

In any of these cases, considering a refactoring strategy can be a great approach. When you are refactoring code, you are writing it in a better way while respecting the input and output data that this code will process. This enables more comprehensive and lower-cost code when it comes to changing it. Martin Fowler indicates some reasons why we should consider refactoring:

  • It improves software design: The more expert your team becomes, the better the design will be. A better software design will deliver not only faster coding but will bring us the opportunity to process more tasks in less time.
  • It makes the software easier to understand: Regardless of whether we are talking about juniors or seniors, good software needs to be understood by each developer you have in the team.
  • It helps us find bugs: While you are refactoring, you are reviewing code. During this process, you will find business rules that may have not been well programmed, so you will probably find bugs. However, do not forget that the basis of refactoring is keeping the behavior, so be sure that it is the correct moment for fixing issues.
  • It makes us program quicker: The result of refactoring will be code that will enable faster development in the future.

The process of refactoring depends on some steps that we shall follow to guarantee good results and minimize errors during the journey:

  • Be sure you have a set of tests to guarantee the correct processing: The set of tests you have will eliminate the fear of breaking the code.
  • Eliminate duplication: Refactoring is a good opportunity to eliminate code duplication.
  • Minimize complexity: Considering you will have the goal of making the code more understandable, following the best practices of programming, as mentioned in Chapter 20, Best Practices in Coding C# 10, will reduce the complexity of the code.
  • Clean up the design: Refactoring is a good time to reorganize the design of your libraries too. Do not forget to update them too. This can be a great way to eliminate bugs and security issues.

As a software architect, you will receive many refactoring demands from your team. The incentive for doing so must be continuous. But you must remind your team that refactoring without following the preceding steps might be risky. So, it is your responsibility to make it happen in a way that can both enable fast programming and less impact, thus delivering real business value.

I have my libraries. How do I promote them?

Considering you have made all the necessary effort to guarantee you have good libraries that can be reused in many of your projects, you will find another difficult situation arises when enabling reusability: it is not simple to let programmers know you have libraries ready to reuse.

There are some simple approaches to documenting a library. As we mentioned when we talked about the development life cycle, documenting is a good way to help developers take notice of the libraries they have. There are two examples of documenting reusable code that we would like to mention here.

Documenting .NET libraries using DocFX

This tool is a good alternative for documenting a library using comments made in its code. By simply adding the NuGet package docfx.console, the tool allows you to create a task that will run once your library has been built:

Interface gráfica do usuário, Texto  Descrição gerada automaticamente

Figure 12.5: docfx.console NuGet library

The output of this compilation is a stylish static website that contains the documentation of your code:

Graphical user interface, text, application  Description automatically generated

Figure 12.6: DocFx result

This website is useful because you can distribute the documentation to your team so that they can search for the libraries you have. You can check the customizations of the output and find more information about it at https://dotnet.github.io/docfx/.

Documenting a Web API using Swagger

There is no doubt that a Web API is one of the technologies that facilitates and promotes code reuse. For this reason, having its documentation well done and, more than that, respecting a standard is good practice and indicates that you are up to date on this approach. To do this, we have Swagger, which respects the OpenAPI Specification.

The OpenAPI Specification is known as the standard for describing modern APIs. One of the most widely used tools for documenting it in an ASP.NET Core Web API is Swashbuckle.AspNetCore.

The good thing about using the Swashbuckle.AspNetCore library is you can set the Swagger UI viewer for your Web API, which is a good, graphical way to distribute the APIs.

We will learn how to use this library in ASP.NET Core Web APIs in the next chapter. Until then, it is important to understand that this documentation will help not only your team but any developer who might use the APIs you are developing.

Use case – reusing code as a fast way to deliver good and safe software

The final design of the solution for evaluating content for WWTravelClub can be checked as follows. This approach consists of using many topics that were discussed in this chapter. First, all the code is placed in a .NET 6 class library.

This means that you can add this code to different types of solutions, such as ASP.NET Core web apps and Xamarin apps for the Android and iOS platforms:

Diagrama  Descrição gerada automaticamente

Figure 12.7: WWTravelClub reuse approach

This design makes use of object-oriented principles such as inheritance, so you do not need to write properties and methods more than once that can be used in many classes; and polymorphism, so that you can change the behavior of the code without changing the name of the method.

To finish, the design abstracts the idea of the content by introducing generics as a tool that can facilitate the manipulation of similar classes, such as the ones we have in WWTravelClub to evaluate content regarding cities, comments, destination experts, and travel packages.

The big difference between a team that incentivizes code reuse and one that does not is the velocity of delivering good software to end users. Of course, beginning this approach is not easy, but rest assured that you will get good results after some time working with it.

Summary

This chapter aimed to help you understand the advantages of code reuse. It also gave you an idea about what is not properly reused code. This chapter also presented approaches for reusing and refactoring code.

Considering that technology without processes does not take you anywhere, a process was presented that helps enable code reuse. This process is related to using already completed components from your library; identifying features in the software requirements specification that are candidates to be designed as library components; modifying the specification considering these features; designing the reusable components; and building the project architecture with the new component library version.

To finish, this chapter presented .NET Standard libraries as an approach to reusing code for different C# platforms, indicating that .NET 6 and new versions shall be used for reusing code in different platforms. This chapter also reinforced the principles of object-oriented programming when reusing code and presented generics as a sophisticated implementation to simplify the treatment of objects with the same characteristics. In the next chapter, we will learn how to apply a service-oriented architecture (SOA) with .NET.

It is worth mentioning that SOA is considered a way to implement code reuse in sophisticated environments.

Questions

  1. Can copy-and-paste be considered code reuse? What are the impacts of this approach?
  2. How can you make use of code reuse without copying and pasting code?
  3. Is there a process that can help with code reuse?
  4. What is the difference between .NET Standard and .NET Core?
  5. What are the advantages of creating a .NET Standard library?
  6. How does object-oriented analysis help with code reuse?
  7. How do generics help with code reuse?
  8. Will .NET Standard be replaced with .NET 6?
  9. What are the challenges related to refactoring?

Further reading

These are some books and websites where you will find more information about the topics covered in this chapter:

Download the example code files

The code bundle for the book is hosted on GitHub at https://github.com/PacktPublishing/Software-Architecture-with-C-10-and-.NET-6-3E.

Join our book’s Discord space

Join the book’s Discord workspace for a Ask me Anything session with the authors:

https://packt.link/SAcsharp10dotnet6

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

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