Labels

Friday, December 3, 2010

Design Principles - SOLID

What are Software Design Principles?

 

Software design principles represent a set of guidelines that helps us to avoid having a bad design. The design principles are associated to Robert Martin who gathered them in "Agile Software Development: Principles, Patterns, and Practices".

 

According to Robert Martin there are 4 important characteristics of a bad design that should be avoided. They are not orthogonal, but are related to each other in ways that will become obvious. they are:

rigidity, fragility, immobility, and viscosity.

 

These four symptoms are the tell-tale signs of poor architecture. Any application that show signs of them, is suffering from a design that is rotting from the inside out.

 

 

 

Rigidity

It is hard to change because every change affects too many other parts of the system.

 

Fragility

When you make a change, unexpected parts of the system break.

 

Immobility

Inability to reuse software from parts of the same project or from other projects.

 

It often happens that one engineer will discover that he needs a module that is similar to one that another engineer wrote. However, it also often happens that the module in question has too much baggage that it depends upon.

 

i.e. It is hard to reuse in another application because it cannot be separated from the current application.

 

Viscosity

Viscosity comes in two forms. High Viscosity is a bad design.

·         Viscosity of the design, &

·         Viscosity of the environment.

 

Avoid hacks to solve a problem.  When the hacks are easy to employ than the design preserving methods, the Viscosity Of The Design/Environment is high.

 

 

 

Single Responsibility PrincipleSRP: 

Principle - A class should have only one reason to change.

 

·         In this context a responsibility is considered to be one reason to change. This principle states that if we have 2 reasons to change for a class, we have to split the functionality in two classes.

·         Each class will handle only one responsibility and on future if we need to make one change we are going to make it in the class which handle it.

·         When we need to make a change in a class having more responsibilities the change might affect the other functionality of the classes.

 

For example, instead of creating a class that has data access code for several different items you should instead separate the data access for each items into a seperate single class.

 

Single Responsibility Principle was introduced Tom DeMarco in his book Structured Analysis and Systems Specification, 1979.

Robert Martin reinterpreted the concept and defined the responsibility as a reason to change.

 

 

Open Close Principle - OCP

Principle - Software entities like classes, modules and functions should be open for extension but closed for modifications.

 

·         OPC is a generic principle. You can consider it when writing your classes to make sure that when you need to extend their behavior you don't have to change the class, rather extend it.

·         The same principle can be applied for modules, packages, libraries.

·         If you have a library containing a set of classes there are many reasons for which you'll prefer to extend it. This extension should happen without changing the code that was already written.

 

For example, you should be able to add a new behavior to a class without affecting the rest of the code. Instead of adding a new case to a switch statement, you should consider re-factoring the code to use separate classes for each case.

 

When referring to the classes, OCP can be ensured by use of Abstract Classes and concrete classes for implementing their behavior. This will enforce having Concrete Classes extending Abstract Classes instead of changing them. Some particular cases of this are Template Pattern & Strategy Pattern.

 

 

Liskov's Substitution Principle - LSP

Principle - Derived types must be completely substitutable for their base types.

That is, a user of a base class should continue to function properly, if a derivative of that base class is passed to it.

 

·         This principle is just an extension of the OCP in terms of behavior, - Meaning that we must make sure that new derived classes are extending the base classes without changing their behavior. The new derived classes should be able to replace the base classes without any change in the code.

 

For example: A typical example that violates LSP is a Square (Derived) class that derives from a Rectangle(Base) class. The Square class always assumes that the width is equal with the height. If a Square object is used in a context where a Rectangle is expected, unexpected behavior may occur because the dimensions of a Square cannot (or rather should not) be modified independently.

 

Liskov's Substitution Principle was introduced by Barbara Liskov in a 1987 Conference on Object Oriented Programming Systems Languages and Applications.

 

 

Interface Segregation Principle - ISP

Principle - Clients should not be forced to depend upon interfaces that they don't use.

 

·         This principle teaches us to take care how we write our interfaces.

·         When we write our interfaces we should take care to add only methods that should be there.

·         If we add methods that should not be there the classes implementing the interface will have to implement those methods as well.

 

For example if we create an interface called Worker and add a method lunch break, all the workers will have to implement it. What if the worker is a robot?

Another example - By having IEnumerable and IDisposable interfaces separate, it's possible for client code to seperately care about dealing with enumerating a collection or disposing of it. Thus not clutter up either operation by mixing two totally different kinds of behaviors.

 

As a conclusion Interfaces containing methods that are not specific to it are called Polluted or Fat Interfaces. We should avoid them.

 

 

Dependency Inversion Principle - DIP

Principle - High-level modules should not depend on low-level modules. Both should depend on abstractions and not concretions.

Abstractions should not depend on details. Details should depend on abstractions.

 

·         Dependency Inversion Principle states that we should decouple high level modules from low level modules, introducing an abstraction layer between the high level classes and low level classes. Further more it inverts the dependency: instead of writing our abstractions based on details, the we should write the details based on abstractions.

·         Dependency Inversion or Inversion of Control are better know terms referring to the way in which the dependencies are realized. In the classical way when a software module(class, framework, …) need some other module, it initializes and holds a direct reference to it. This will make the 2 modules tight coupled. In order to decouple them the first module will provide a hook(a property, parameter, …) and an external module controlling the dependencies will inject the reference to the second one.

·         By applying the Dependency Inversion the modules can be easily changed by other modules just changing the dependency module.

 

Factories and Abstract Factories can be used as dependency frameworks, but there are specialized frameworks for that, known as Inversion of Control Container.

 

Hope this helps.

 

Reference :

Design Principles

Design Principles – By Example

 

 

Thanks & Regards,

Arun Manglick

Design Principles - SOLID - Proof Of Concept

Open Close Principle - OCP

Principle - Software entities like classes, modules and functions should be open for extension but closed for modifications

 

Consider below code.  The LogOn function must be changed every time a new kind of modem is added to the software.

Worse, since each different type of modem depends upon the Modem::Type enumeration, each modem must be recompiled every time a new kind of modem is added.

 

 

 

struct Modem

{

  enum Type {hayes, courrier, ernie) type;

};

 

void LogOn(Modem m,string pno)

{

  if (m.type == Modem::hayes)

    DialHayes((Hayes&)m, pno);

  else if (m.type == Modem::courrier)

    DialCourrier((Courrier&)m, pno);

  else if (m.type == Modem::ernie)

    DialErnie((Ernie&)m, pno)

 

// ...you get the idea

}

 

 

 

 

Of course this is not the worst attribute of this kind of design.

Programs that are designed this way tend to be littered with similar if/else or switch statement.

Every time anything needs to be done to the modem, a switch statement if/else chain will need to add/select the proper functions to use.

When new modems are added, or modem policy changes, the code must be scanned for all these selection statements, and each must be appropriately modified.

 

Thus here - Logon, is modified to be extended – Breaking OCP.

 

OCP Solution :

 

 

 

 

class LogOn

{

  public static void LogOn(Modem m)

  {

    m.Dial();

  }

}

class MainApp

{

  static void Main()

  {

                LogOn.LogOn(new HayesModem());

                LogOn.LogOn(new CourierModem ());

                LogOn.LogOn(new ErnieModem ());

  }

}

 

 

Here the LogOn function depends only upon the Modem interface.

Additional modems will not cause the LogOn function to change.

Thus, we have created a module that can be extended, with new modems, without requiring modification.

 

 

Interface Segregation Principle - ISP

Principle - Clients should not be forced to depend upon interfaces that they don't use.

 

Below shows a class with many clients, and one large interface to serve them all.

Note that whenever a change is made to one of the methods that ClientA calls, ClientB and ClientC may be affected.

It may be necessary to recompile and redeploy them. This is unfortunate.

 

 

 

 

 

 

class Service : IServiceA,IServiceB,IServiceC

    {

        public string Swim()

        {

            return "Animal Swimming";

        }

 

        public string Fly()

        {

            return "Animal Flying";

        }

 

        public string Run()

        {

            return "Animal Running";

        }

    }

class ClientA

{

   Service service;

 

   public ClientA(Service service)

   {

        this. service = service;

   }

 

   public string Execute()

   {

      return running.Swim();

      return running.Fly ();

      return running.Run();

 

   }

}

 

class ClientB

{

   Service service;

 

   public ClientA(Service service)

   {

        this. service = service;

   }

 

   public string Execute()

   {

      return running.Fly();

      return running.Swim();

      return running.Run();

 

   }

}

 

class ClientC

{

   Service service;

 

   public ClientA(Service service)

   {

        this. service = service;

   }

 

   public string Execute()

   {

      return running.Run();

      return running.Swim();

      return running.Fly();

   }

}

 

Here you can see that: Even the ClientA, does not the need the Fly and Run services, it can access those.

Thus if any change is made in Fly and Run, CleintA also need to be changed.

 

Same applies for CleintB & C.  This breaking the rule.

 

 

 

A better technique is shown below. The methods needed by each client are placed in special interfaces that are specific to that client.

Those interfaces are Multiply Inherited by the Service class, and implemented there.

Now, If the interface for ClientA needs to change, ClientB and ClientC will remain unaffected. They will not have to be recompiled or redeployed.

 

 

 

 

 

 

public interface IServiceA

{

   string Swim();

}

 

public interface IServiceB

{

   string Fly();

}

 

public interface IServiceC

{

   string Run();

}

 

 

class Service : IServiceA,IServiceB,IServiceC

    {

        public string Swim()

        {

            return "Animal Swimming";

        }

 

        public string Fly()

        {

            return "Animal Flying";

        }

 

        public string Run()

        {

            return "Animal Running";

        }

    }

 

 

class ClientA

{

   IServiceA running;

 

   public ClientA(IServiceA running)

   {

        this.running = running;

   }

 

   public string Execute()

   {

      return running.Swim();

   }

}

 

class ClientB

{

   IServiceB animal;

   public ClientB(IServiceB animal)

   {

      this.animal = animal;

   }

 

   public string Execute()

   {

       return animal.Fly();

   }

}

 

class ClientC

{

  IServiceC animal;

 

  public ClientC(IServiceC animal)

  {

     this.animal = animal;

  }

 

  public string Execute()

  {

     return animal.Run();

  }

}

 

Here you can see that: The ClientA, has access to only the relevant Swim service. It cannot access the Fly and Run services.

Thus if any change is made in Fly and Run, CleintA will not be affected.

 

Same applies for CleintB & C.  This serving the ISP rule.

 

 

 Hope this helps.

 

Thanks & Regards,

Arun Manglick

Wednesday, December 1, 2010

Architecture - What is Scalability

The very first question in any Architecture discussions/interviews – How to make your application Scalable.

Before answering, it's better to know what is 'Scalability'.

 

Few definitions:

 

ü  In software engineering, scalability is a desirable property of a system, which indicates its ability to handle growing amounts of work in a graceful manner.

ü  Indicates the capability of a system to increase total throughput under an increased load(more users) when resources (typically hardware) are added.

ü  A term that refers to how well a hardware and software system can adapt to increased demands.

ü  Refers to the ability of an application to increase total throughput to handle the growing needs of data flow.

ü  It is the ability of a computer application or product (hardware or software) to continue to function well when it is changed in size or volume, in order to meet a user need.

 

Scalability is a design concern of Distributed Applications and not Stand-Alone Applications.

 

Distributed applications are a step beyond traditional client-server applications. Distributed applications are applications that are designed as n-tier applications.

Such distributed application architectures promote the Design Of Scalable Applications by sharing resources, such as business components and databases.

 

Scaling comes in two flavours - Scale Up (vertically) vs.  Scale-out (horizontally).

 

Scale-Up: (Adding More Memory/Processors – To a Single Machine)

 

Scale up means to add resources to a single node in a system.

I.e. Scaling up includes adding more memory, adding more or faster processors, or simply migrating the application to a more powerful, But Single Machine.

Such vertical scaling of existing systems also enables them to use virtualization technology more effectively.

 

Defeciences:

 

Scaling up requires more expensive hardware, as single machine is upgraded. It's like using Smart/Expensive Server having Symmetric Multi-Processor (SMP) configurations.

 

Upgrading a hardware component in a single machine simply moves the processing capacity limit from one part of the machine to another part.

Adding more processors does not add performance in a linear fashion. Instead, the performance gain curve slowly tapers off as each additional processor is added.

For machines with symmetric multi-processor (SMP) configurations, each additional processor incurs system overhead. Consequently, a four-processor machine will not realize a 400 percent gain in capacity over the uniprocessor version. Once you have upgraded each hardware component to its maximum capacity, you will eventually reach the real limit of the machine's processing capacity.

 

Scaling up also presents other potential problems. Using a single machine to support an application creates a single point of failure, which greatly diminishes the fault tolerance of the system. While methods, such as multiple power supplies, may implement redundancy in a single-machine system, these options can be expensive.

 

 

cid:image001.png@01CB6BD2.41D0FAB0

 

 

Scale-Out: (Adding more Machines/Servers)

 

Scale Out means to add more nodes to a system, such as adding a new computer to a distributed software application.

 

Scaling out is less expensive, as it requires commodity PC hardware to distribute the processing load across more than one server.

Although scaling out is achieved using many machines, the collection essentially functions as a single machine.

By dedicating several machines to a common task, application fault tolerance is increased.

 

cid:image002.png@01CB6BD3.EE33BA30

 

Developers and administrators use a variety of Load Balancing Techniques to scale out with the Windows platform.

Load balancing allows a site to scale out across a cluster of servers, making it easy to add capacity by adding more Replicated Servers.

It also provides Redundancy, giving the site failover capabilities so that it remains available to users even if one or more servers fail (or need to be taken down for maintenance).

Scaling out provides a method of scalability that is not hampered by hardware limitations. Each additional server provides a near linear increase in scalability.

 

The key to successfully scaling out an application is Location Transparency. If any of the application code depends on knowing what server is running the code, location transparency has not been achieved and scaling out will be difficult. This situation is called Location/Server Affinity.

 

Of course, from the administrator's perspective, scaling out also presents a greater management challenge due to the increased number of machines

 

Hope this helps.

 

Regards,

Arun Manglick

n-Tier Architecture

Introduction 

This document describes the details of four tier implementation of the new architecture.

The scope of this document is demonstrating the four tier implementation of the new architecture.

This document is primarily targeted for the developers and architects working on the Veriphy applications. It provides a useful perspective to other stakeholders such as managers, and the customer.

Overall Description

In this scenario, the solution is divided across four separate tiers, or physical machines.

 

·         The client workstation represents first tier that uses a browser application to interact with the Web server.

·         The Web server represents a second tier that contains the presentation layer, which is responsible for handling requests from the user and interacting with the business layer to implement the request.

·         The application server represents the third tier with service, business, and data access layers.

·         The fourth tier in this design is the database server.

 

Tiered Diagram

 

 

 

·         Browser interaction with the Web Server uses standard HTTP GET and POST requests.

·         The presentation layer uses a request-based / message-based protocol to interact with the service layer.

·         The Service Layer uses a Facade call to interact with the business layer.

·         Stand-alone ASP.NET Web application that supports basic CRUD operations.

·         Presentation and Business logic are distributed across physical boundaries.

·         DAL implementation will be implemented using LINQ.

·         The application uses data from an existing database schema.

·         Tables and views in the database define data structures used by the application

 

 

Another View

 

 

 

 

Detailed Tiered/Layered

 

 


The following is a summary of the patterns used by this scenario:

 

·         User interface processing is handled by a Request-Response pattern.

·         The user interface is composed of multiple controls, with some that can be bound to data elements.

·         An Http-Get request / Proxy is used to communicate between the presentation layer and service layer

·         The Data Transfer Object (DTO) pattern is used to package multiple data structures into one.

·         The Service Layer provides translation between internal and external data structures.

·         The Business Layer uses a façade pattern to support coarse-grained message-based operations.

·         Transactions and Business Logic are managed by objects in the business layer.

·         Business entities are defined using the Table Module pattern (LINQ).

·         The Table or Row Data Gateway pattern is used to provide a data access interface.

·         The Query Object pattern is used to support the generation of SQL queries.

 

 

 

Technical Details

 

 

Client Workstation

 

 

Target browser is Internet Explorer 6.x and higher

 

Browser will initiate the further flow.

 

 

Web Server

 

 

ASP.NET Page Control

ASP.NET Page controls are used to define each page of the web application.

Standard ASP.NET page processing is used to handle control events.

 

Composite View

Page, server and user controls represent a Composite View.

Bound Data Control

Control that can be bound to a data structure, which is used to provide data displayed by the control.

Proxy

Provides a local interface used to interact with services on the application tier.

Adding a service reference will generate classes used to interact with the service. From a coding perspective the proxy provides a local interface that hides details related to interaction with the service.

 

Proxy is used when the presentation layer is required to communicate using WCF contracts.

 

 

Application Server – Service Layer

 

 

Service Interface

The service layer can be Class Library Project or could be defined using WCF service, data, message, and fault contracts.

 

Operations exposed by the service are application scoped. For example, instead of providing multiple operations to return demographic information you would provide one operation that returns all data related to demographic information.

 

Data Transfer Object

Used to combine multiple data structures into a unified view that can be passed across physical and logical boundaries.

 

Entity Translator

Implement an object that transforms message data types to business types for requests and reverses the transformation for responses.

 

Classes that provide operations to translate between business entities and WCF data contracts.

 

Translate DTO to Business Entity Objects.

 

Public LINQ.DataManager.Order TranslateOrderToOrderDTO();

 

               

Application Server – Business Layer

 

 

Façade Interface

The business layer implements a façade with coarse grained operations.

Implement a unified interface to a set of operations to reduce coupling between systems.

An Interface type is used to define the façade interface.

The Façade acts as a boundary that catches all unhandled exceptions that may occur when processing business operations.

 

 

public interface BusinessFacade

    {

        Data.Order GetData();

        int SaveData(LINQ.Data.Order businessEntity);

        bool DeleteData(int businessEntityID);

    }

 

 

Business Process Objects

Business process objects are used to handle requests made to the business layer.

Business process objects allow you to Implement Business Rules And Provide Transaction support if required.

Business operations that need to be included in a transaction are combined in a single operation exposed by a business process object that implements the transaction script pattern.

 

 

public bool SaveData(LINQ.Data.Order businessEntity)

{

    string sError = string.Empty;

    bool success = false;

    int orderId = -1;

 

    try

    {

                BeginTransaction();

 

                if (orderId == -1)

                {

                    orderId = LINQ.DataManager.FindNextOrderID();

                    if (orderId == -1)

                    {

                                sError = "Failed to retrieve new Dealer ID. Rolling Back Transaction";

                                RollbackTransaction();

                                return success;

                    }

 

                    orderId = LINQ.DataManager.InsertOrder(businessEntity);

                    if( orderId == 0)

                    {

                                sError = "Failed to insert Dealer ID. Rolling Back Transaction";

                                RollbackTransaction();

                                return success;

                    }

                }             

 

                CommitTransaction();

                success = true;

 

    }

    catch (Exception ex)

    {

                RollbackTransaction();

    }

 

    return success;

}

 

 

 

Application Server – Data Access Layer

 

 

Table Module / Business Entities

Business entity that represents a table or view within a database and provides operations to interact with the table or view.

Business rules are not implemented within the business entities.

The business entities are just data containers. Business rules are handled by business processing components.

 

LINQ DataManager

Separate classes are used to define LINQ query objects.

It behaves like a gateway object - defined for each business entity in the design.

The gateway encapsulates all logic related to interacting with the associated DB table or view.

 

 

public int InsertOrder(LINQ.Data.Order order)

{

     order.InstitutionID = _institutionId;

    _dataContext.Orders.InsertOnSubmit(order);

    _dataContext.SubmitChanges();

 

    return order.OrderID;

}

 

 

 

 

Database Server

 

 

SQL Server DB

Tables and views are accessible to the Data Access Layer using LINQ ORM.

 

 

 

Reference: Link1, Link2

 

Hope this helps.

 

Thanks & Regards,

Arun Manglick