Click here to Skip to main content
14,355,397 members

Modular Software Development In C#

Rate this:
4.69 (5 votes)
Please Sign up or sign in to vote.
4.69 (5 votes)
24 Jul 2019CPOL
This article briefly discusses modular software development in C#.

Table of Contents

  1. Introduction
  2. Types Of Specifications
  3. Modules
  4. No Module Development
  5. Modular Programming
  6. Predicates and Types
  7. Contracts
  8. Typed Programming
  9. Interfaces and Abstraction
  10. Signatures in Other Languages
  11. History

Introduction

Image 1

Software design promotes a set of programming qualities:

  • Abstraction: The ability to represent parts of a program with a summary of their essential characteristics
  • Encapsulation: The possibility to expose public parts and hide private parts of an abstraction
  • Modularity: The ability to decompose a program into a set of cohesive and loosely coupled components

Some goals:

  • Maintainability: Is it easy to modify and extend a program?
  • Reusability: Is it possible to avoid code duplication?

Modularity helps to have extensible, modifiable, portable, maintainable, reusable, understandable and flexible software. It allows new features to be seamlessly added when desired, and unwanted features to be removed, thus simplifying the user-facing view of the software. It allows several developers to work on different modules concurrently. It also allows to test modules independently. Furthermore, large projects become easier to monitor and to control.

C# such as C, Java and other programming languages allow to create modular software through different ways. In this article, we will briefly explore modular software development through interfaces in C#.

Types of Specifications

Code specification may take different forms:

  • Functional specification:
    • It should be possible to define sets containing any kind of objects of the same type.
    • It should be possible to add and remove elements from a set, and to tell if an element belongs to a set or not.
  • Non-functional specification:
    • The implementation should run efficiently on current computers.
    • The implementation should handle correctly sets of arbitrary sizes.

Here comes a problem of validation: How can specifications be checked?

Below is the Interface/Type specification:

public interface ISet<T>{
  ISet<T> Add(ISet<T> s, T e);
  ISet<T> Remove(ISet<T> s, T e);
  ISet<T> Empty();
  bool IsEmpty(ISet<T> s);
  bool Find(ISet<T> s, T e);
  int Length(ISet<T> s);
}

Below is the formal specification:

IsEmpty(Empty()) = true
IsEmpty(Add(s, e)) = false
Find(Empty(), e) = false
Find(Add(s, e), e) = true
Find(Add(s, e), e) = Find(s, e)
Add(s, e) = s [if Find(s, e) = true]
[unconstrained otherwise]
Remove(Empty(), e) = Empty()
Remove(Add(s, e), e) = s
Remove(Add(s, e), f) = Add(Remove(s, f), e)

Modules

A module is a construct representing a unit of code (set of types, values, functions and any expression allowed by a language) and satisfying:

  • Interface: A module may publicly provide and require a set of components.
  • Encapsulation: A module may hide or make abstract some of its components.

Sets of modules are meant to be connected according to the dependencies induced by their interfaces:

  • Independence: A module should depend only on the interfaces of its dependencies.

Many programming languages provide modules complying only to a subset of these properties:

  • Interfaces may provide different levels of verification: Names only (Racket, Python), types (C, OCaml).
  • Encapsulation may not exist (cf. Python module privates).
  • Interfaces must sometimes be shipped with the module (Racket, Haskell) or live as independent citizens (C, OCaml).

In the following article, we will investigate the capabilities of C# in regard to modules.

No Module Development

All the code is written in a single unit, with few specifications.

Below is an example:

public T[] Empty<T>(){
  return new T[]{};
}
public bool IsEmpty<T>(T[] s){
  return s.Length == 0;
}
public T[] Add<T>(T[] s, T e){
  var l = new List<T>(s);
  l.Add(e);
  return l.ToArray();
}
public T[] Remove<T>(T[] s, T e){
  var l = new List<T>(s);
  l.Remove(e);
  return l.ToArray();
}
public bool Find<T>(T[] s, T e){
  return s.Contains(e);
}
public int Length<T>(T[] s){
  return s.Length;
}

Favorable features:

  • REPL (simplifies incremental development)
  • Extensible language (simplifies writing ad-hoc code)

Problems:

  • Parts of the code with different purposes are all mixed up in a single file (example: Implementation and tests)
  • Hinders code reuse/modification of a subset (e.g. modification of the set representation)
  • Complexifies the verification of the code (All or nothing)
  • Not adapted to separate compilation, separate testing, team development

Modular Programming

Modular programming breaks the code into a set of cohesive and loosely coupled modules, that
shall be composed depending on the specification.

The relationship interface/implemented class is similar to module/signature.

Below is the Interface/Type specification:

public interface ISet<T>{
  ISet<T> Add(ISet<T> s, T e);
  ISet<T> Remove(ISet<T> s, T e);
  ISet<T> Empty();
  bool IsEmpty(ISet<T> s);
  bool Find(ISet<T> s, T e);
  int Length(ISet<T> s);
}

Below is a client module:

public class MySet<T>:ISet<T>{
  public ISet<T> Add(ISet<T> s, T e)    {  /* ... */ }
  public ISet<T> Remove(ISet<T> s, T e) {  /* ... */ }
  public ISet<T> Empty()                {  /* ... */ }
  public bool IsEmpty(ISet<T> s)        {  /* ... */ }
  public bool Find(ISet<T> s, T e)      {  /* ... */ }
  public int Length(ISet<T> s)          {  /* ... */ }
}

And below is a test module:

public class MySetTest{
  public void AddTest()     {  /* ... */ }
  public void RemoveTest()  {  /* ... */ }
  public void EmptyTest()   {  /* ... */ }
  public void IsEmptyTest() {  /* ... */ }
  public void FindTest()    {  /* ... */ }
  public void LengthTest()  {  /* ... */ }
}

Advantages:

  • Facilitates code reuse: Both tests and clients can make use of the same implementation module.
  • Allows multiple implementations: The client can select an appropriate implementation module without modification of its code.
  • Ensures that required functions are effectively provided
  • Allows several developers to work on different modules concurrently
  • Allows to test modules independently
  • Large projects become easier to monitor and to control

Predicates and Types

A type is a subset of the values of the language. For example: The bool type is the set {true, false}.

A predicate is a function taking any possible value and returning a boolean:

  • A predicate is the characteristic function of a type.
  • Predicates are usually the sign of a language with dynamical type checking.

Contracts

In C#, according to the official documentation, contracts provide a way to specify preconditions, postconditions, and object invariants to the code. Preconditions are requirements that must be met when entering a method or property. Postconditions describe expectations at the time the method or property code exits. Object invariants describe the expected state for a class that is in a good state.

Code contracts include classes for marking the code, a static analyzer for compile-time analysis, and a runtime analyzer. The classes for code contracts can be found in the System.Diagnostics.Contracts namespace.

The benefits of code contracts include the following:

  • Improved testing: Code contracts provide static contract verification, runtime checking, and documentation generation.
  • Automatic testing tools: You can use code contracts to generate more meaningful unit tests by filtering out meaningless test arguments that do not satisfy preconditions.
  • Static verification: The static checker can decide whether there are any contract violations without running the program. It checks for implicit contracts, such as null dereferences and array bounds, and explicit contracts.
  • Reference documentation: The documentation generator augments existing XML documentation files with contract information. There are also stylesheets that can be used with Sandcastle so that the generated documentation pages have contract sections.

Typed Programming

A type system is a formal method applied to a program which aims at classifying elements of the program with types so as to guarantee some correctness of its behavior.

Depending on the language and its compiler, type systems may come in different flavours:

  • Dynamic type checking: All verifications are done at runtime, no type annotations necessary (Racket, Python, Ruby).
  • Static type checking: All verifications are done at compile time, type annotations are either required (C#, C, Java) or inferred (OCaml, Haskell).

Advantages:

  • The types are checked statically.
  • The Typed type system is pretty expressive.

Interfaces and Abstraction

Abstract data types are:

  • An abstract and independent representation of a component
  • Possibly multiple different implementations for the same abstraction
  • Possibly multiple clients for the same abstraction

Signatures in Other Languages

Interfaces in Java:

interface Set<T>{
  Set<T> add(Set<T> set, T e);
  Set<T> remove(Set<T> set, T e);
  Set<T> empty();
  boolean is_empty(Set<T> set);
  boolean find(Set<T> set, T e);
  int length(Set<T> set);
}

Signatures in OCaml:

module type SET = sig
  type ’a set
  val add       : ’a set → ’a → ’a set
  val remove    : ’a set → ’a → ’a set
  val empty     : unit → ’a set
  val is_empty  : ’a set → bool
  val find      : ’a set → ’a → bool
  val length    : ’a set → int
end

History

  • 24th July, 2019: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Akram El Assas
Architect
Morocco Morocco
Akram El Assas graduated from the french engineering school ENSEIRB located in Bordeaux, a city in the south of France, and got his diploma in software engineering in 2010. He worked in France for Mediatvcom, a company specialized in audiovisual, digital television and new technologies. Mediatvcom offers services such as consulting, project management, audit and turnkey solutions adapted to the needs of customers. Akram worked mainly with Microsoft technologies such as C#, ASP.NET and SQL Server but also with JavaScript, jQuery, HTML5 and CSS3. Akram worked on different projects around digital medias such as Media Asset Management systems, Digital Asset Management systems and sometimes on HbbTV apps.

Comments and Discussions

 
-- There are no messages in this forum --
Article
Posted 24 Jul 2019

Tagged as

Stats

8.2K views
120 downloads
15 bookmarked