Click here to Skip to main content
13,199,852 members (65,660 online)
Click here to Skip to main content
Add your own
alternative version

Stats

19.8K views
185 downloads
38 bookmarked
Posted 22 Apr 2017

Application Architecture - What's The Matter With Being STUPID Before Being SOLID

, 24 Apr 2017
Rate this:
Please Sign up or sign in to vote.
This topic will cover the bad design practice using STUPID and good design practice using SOLID. Detail explanation for Single Single Responsibility Principle, Open and Closed Principle, Liskov Substitution Principle, Interface Segregation Principle and Dependency Inversion (DI) Principle.

Application Design Principles

Audience of the Software Design Topics - Application Architect, Developers.

What will you learn from the topics

The following concepts will be covered in this topic - 

  1. Stupid Design for bad practice
    • Singleton
    • Tight Coupling
    • Un-testability
    • Premature Optimization
    • In-descriptive Naming
    • Duplication
  2. Solid Design for good practice
    • Single Responsibility Principle
    • Open and Closed Principle
    • Liskov Substitution Principle
    • Interface Segregation Principle
    • Dependency Inversion Principle

Let's Drilldown The Basic Concept

Wait a minute!!...why am I talking? No! Today, I'll not discuss the WAIT principle at-all. Software design and implementation should be quick and simple. If it can accomplish all of your requirements for now then it is good enough to go. Yeah, I'm talking about the "Good Enough" principle. Even if, at the very first if we start and implement something poorly then it's good enough. That's how we learn it.  But what about the future? You should keep your one eye for the future of this system. So, if we practice some stupid staff then its fine. But we should have to improve ourselves for the future.

STUPID Design

Apologize me, if I hurt your feelings; but I did the same thing like you. Anyway, what does the very word STUPID mean? The STUPID means -

  • Singleton Pattern
  • Tight Coupling
  • Un-testability
  • Premature Optimization
  • In-descriptive Naming
  • Duplication

Singleton Pattern

You are already familiar with this pattern. It instantiates a single static object and ensures to provide a global access point.

This is also one of the favorite pattern to many developers. So, I will not use any bad word for it. I just want to say, Singleton pattern itself is not the problem; problem, if you don't know - when, where and how you should use it.

Problems -

  • Unit Testing: If you are used to the unit testing then you know that it is very hard to test and generally it can't possible to make a mock for it. Because, it is tightly coupled. It hides the dependency in the code of the application.
  • Global Instance: It hides the dependency in the code of the application. Many peoples think that it could be an anti-pattern if you incorrectly implement and use it. 
  • It violates the Single-Responsibility principle and it controls the object creation and lifecycle by itself.

So if you need a single instance then use it and make sure it is thread safe. But be careful and don't use it globally all over the place.

Tight Coupling

In the class diagram,

  • Customer Class needs classes 'CheckingAccount' and 'SavingAccount' classes to do its work.
  • "Customer" Class can't do its work without classes 'CheckingAccount' and 'SavingAccount'.

In the example, CheckingAccount and SavingingAccount are tightly couple with the customer class. This implementation violates the Opened-Closed principles where your classes should be open for extension but closed for modifications. Anyway, forget the principle. Just think that your checking-account class is fixed in this design and if you need another implantation of the checking-account or saving-account then what should you do?

Finally, we can say that -

  • Tight coupled design is very hard to reuse and don't allow to extend in the future.
  • It is difficult to test. Most of the time, object mocking is not possible for unit testing.

So, the solution is that it should be loose coupled.

Un-testability

According to the first Principle of the unit testing we that Test the logic of the class only, nothing else

When you are going to test a class, you should not have dependency on database, file, registry, web services, etc. You should be able to test any class in complete "isolation" and it should be designed to support complete "isolation."

According to this principle mock out all external services and state and remember unit test NEVER uses-

  • configuration settings
  • a database
  • Another Application/Service/network I/O or file system;
  • logging

Let's see the example - suppose, you have a method 'IsValidUser' to verify the user name and password for login.

Now if you look at the line number 15 and 17 then you will see class 'UserLogIn' has a dependency to the class 'UserDataAccess' and it is tightly coupled. Now the problem is you need to test a method 'IsValidUser' of a class class 'UserLogIn' and you have to avoid the dependency. Because if you call 'IsValidUser' then it will call the method 'GetUserInfoByUseeName(userName)' and according to the unit test principle if we have a dependency to a method of another class then we have to mock that object class. It means that we have to inject some dummy data to that object. In this example we need to inject dummy data for the 'GetUserInfoByUseName(userName). If we do so then we will able to verify the logics of the method 'IsValidUser' in the 'UserLogIn' class.

So, to avoid the tight coupling code, we need to refactor the code for loose coupling.

Premature Optimization

Say, we are imagining that we are incrementing a counter value. To increment the value, we can use either pre-increment (++counter) or post-increment (counter++). Say, pre-increment is more efficient then post-increment. So, which one should we use. If we use the wrong one then what happened?

Optimization has different labels from very high level to the low levels. Choosing good architecture for the application, abstraction of each layers using loose coupling. It also depends on to choose the right data structure and correct algorithm. Efficient coding and many more stuff are connected with it. So, I am avoiding it now to make it easy.

In-descriptive Naming

More or less we all know about the clean code, naming convention and best practices. It can start from the solution name of your project to name of your component, class, method, attributes, property, variables and so on. Are you writing codes for yourself only? If you are not there and in future another one comes then how he/she will understand your code and design? 

For example, I have a class and its name is DaEmp. So, how do you know what is it mean? I'm sure, nothing. Because, you don't like to guess only.

But say, I mean that this class will be used for data accessing from employer table of the database. So, if I re-name it like DataAccessForEmployer then it will make a sense at-least and you don't need to ask me.

Again suppose, I have an int variable c then how do you know what is it for? But I mean counter. So, keep the variable name int counter. Avoid the abbreviation like emp. What's the problem if you write employer instead of "emp". Don't write code for your-self only, write code for another peoples and keep it readable. Apologize me.

Duplication

In the bellow example we are repeating some properties. Not only in your model classes but also it could happen to your methods or anywhere of the classes or in the project.

In this particular scenarios, the solution will be - say, we could create a base class for the Address and inherit it into your model classes like GeneralClient and CorporateClient classes. Note that if you use only Id in your class then best practice is that use ClassName+Id instead only ID. I mean if class name is Address, Employee then use AddressId, EmployeeId instead of only Id.

Always better to remember - "Don't Repeat Yourself" principle.

Solid Design

It's time to wash out the dust from the design. Yes, if you want to be good architect then you should have to follow some design pattern and principles. It could be your own pattern and principles, does not matter. You have to make sure that your design is flexible like -

  • Easy to change
  • Hard to break
  • Easy to reuse
  • Easy to extend
  • Easy to read and understand
  • Very simple, neat and clean design

For class designing purpose, now I will discuss only SOLID principles. What does the very word SOLID mean?

So, SOLID means -

  • Single Responsibility Principle
  • Open-Closed Principle
  • Liskov Substitution Principle
  • Interface Segregation Principle
  • Dependency Inversion Principle

Single Responsibility Principle

A class should have only one reason to change.

I know you understand what it means. But I am not clear; so follow me and confirm me that am I right or wrong? Let's see the example,

In the class diagram, I'm pointing to the SwitchForFanLight class. It has two dependencies light and fan.

Intentionally I have picked up this confusing example to give you an idea about the design. It is in the middle of the good and bad design. In this design scenarios, Fan and Light are loosely-coupled with the SwitchForFanLight class. Do you want to see how? Alright let's see the implementation of the SwitchForFanLight class bellow and technically it has no problem. We can inject any of the implantation of the ISwitch interface.

Again, if you see the implementation of the Fan and Light class then we are good for that.

Okay, no more drama! Come to the main point, I have one switch and if I turn on the switch then it turns on both of the fan and light. Similarly, if I turn off the switch then it turns off both of the light and fan. I mean one switch for both fan and light. I am saving my time and cost. One switch for both of them. I am a good engineer. How funny huh!

But I did not realize that one day I would feel too cold and I wouldn't need fan; but I need light. Now what? If I turn off the fan then I have no light. Another day, it was sun shine outside and it brightest my room. So, I didn't need light and I turned off the light and it stopped the fan too. But I need fan because of the temperature. It was too hot.

Realization- :

Now I am in problem with this design. Because one switch has two responsibility at the same time. If we turn on the switch then one method is called from the fan object and another one is called from the light. Again if anything happen to the light class, say, implementation bug or compile time bug then light and fan both of them will be effected. So, I have violated the single-responsibility principle in the SwitchForFanLight class.

So, now there we go. I'm bound to change my implementation. Now I have speared the switch and that is one switch for fan and another switch for light. Cool Huh!

 I have learned that one class should have only one responsibility and it should have only one reason to change.  By the way, it is called 'Single Responsibility' principle.

Open-Closed Principle

It states that "Modules and Methods should be open for extension but closed for modifications."

Wait-Principle -

Disclaimer: I'm a lazy developer and always prefer short-cut way. So, I want to use my previous example. That's how, I don't need to implement another project as well as don't need to explain again. I don't want to keep pressure on your brain. Alright, now we are good to go.

In my previous example, we have been having a problem with SwitchForFanLight class and I don't want to use this class anymore. But I don't have time to change that class and even I don't know where I used that reference. If I change it now then I don't know where it can effect. So, better for me just keep it alone and let it go.

Anyway, that time, when I have been having problem then I have extended my design and I have created another two classes and that are SwitchForFan and SwitchForLight.

It was easy for me, because, I had a base interface ISwitch and in the SwitchForFan and SwitchForLight classes, I have inherited the ISwitch interface as a base class.

It means that I have extended the SwitchForFan and SwitchForLight classes according to my current requirements and I didn't modify the old SwitchForFanLight class at-all.

Now look at image which is given bellow. There is a SwitchBoardForBadDesign class which contains two methods TurnOn and TurnOff. Both of these methods have some if and else-If statements which are used for turning on or turningOff the light or fan or both according to the SwitchType.

Now come to the main point, in future, if you need another implementation of the ISwitch then you need to add another If-Else statement into your TurnOn and TurnOff methods. So, there you go. This is the violation of the Open-Close principle. Because it is forcing you to modify your codes.

Now what is the solution for these issue??

Solution:

Let’s look at the abstract switch class which is given bellow –

So, I have fixed that issue into the SwitchBoardForGoodDesign class.  In this class I have inherited the abstract Switch class and overrided the TurnOn and TurnOff methods. 

Now in future, if we add one more implementation of the ISwitch say, GreenLight or RedLight then we don’t need to touch the SwitchBoardForGoodDesign class.  So, now it is closed for the modification as well as we can extend our ISwitch in future.

That's how, Open-Close principle works. It helped me to get rid of that problem.

Liskov Substitution Principle

It states that "Derived classes must be substitutable for their base classes".

Make sure that new derived classes are extending the base classes without changing their behavior.

I want to share one of my worse experience with this principle. But again, I'm a lazy developer. So, don't worry, I'll not pressure you.

I am used to the British system to my daily life. But Now I am in USA. So, I have got one project and my job was very easy. I just need to extend the functionally.  During the development, they already told me that I will get one abstract class and its name is 'Switch'.

They have another class kSwitch and my task is that I have to implement UsaSwitch. Apologize me for kSwitch. It is a bad naming convention. But I am using this just for the example.

I am including the class diagram-

The implement of the K-switch was given. But I don't need to see that implementation; because I know how switch works.

For me, it is very simple to implement and I have done that. So, after the unit testing I have send it to the QA team. But when they have started to test it, they have got bug. They told me, it was not working according to their expectation. They explained that when they turned up the switch, the electricity was not passing. Similarly, when they turned down the switch, the electricity was not stopping its flow.

Then I have read the document to know about the behavior of the switch and I understand that I have changed the behavior of the base class.

The UsaSwitch class was implemented like this way -

Finally, I have learned that it was the violation of the Liskov Substitution Principle. Therefore, in USA, Turn-Up means start power supply. Turn-Down means stop power supply. In UK, Turn-Up means Stop Power supply. Turn-Down means Start Power.

So, don't change the meaning of the base class during the extension. You will not get any compilation error but it can breaks your code during run-time.

Interface Segregation Principle

It states that - "Clients should not be forced to depend on interfaces that they don't use".

Say,we have an IAnimal interface. There is no doubt that Moose, Red Deer, Dog and Cat all are animals. Let's see the class diagram of these -

The IAnimal interface has the following properties -

The Moose and RedDeer model classes are implemented from the IAnimal interface.

Similarly, the Dog and Cat model classes are also implemented from the IAnimal interface.

Now the problem is Moose and RedDeer has horns; but Dog and Cat has no horns. IAnimal interface forces to implement the SizeOfHorns property. So, this is the violation of the Interface Segregation Principle for the Dog and Cat classes. Although, we don't have any complains for Moose and RedDeer classes.

So, according the principle we have to remove the SizeOfHorns from the IAnimal interface then Dog and Cat classes will be fine. On the other hand, we will add SizeOfHorns properties into the implemented classes of the Moose and RedDeer or somewhere according to our design plan.

Dependency Inversion Principle

Real Life Scenario

Just for easy understanding let's say, you have different implementation of the Data Access Layer (DAL). For example, IDataAccess has two implementations and these are DataAccessForSql and DataAccessForOracle. Depending on the customers, sometimes, you need DataAccessForSql or sometimes you need DataAccessForOracle. But if your classes are tightly coupled then tight coupling design will not help you in this scenarios.

Again you have more than one payment systems like PayPal, Visa and MasterCard. You need different implementation of your business logics depending on the payment system. For example, say, IPayment has some implementation like PaymentForMasterCard, PaymentForPayPal and PaymentForVisa. So, you need to switch from one to another depend on the preference of your customer. Tight coupling design will not give you this facilities.

So, solution is the Dependency Inversion.

Scenarios -2

Look at the bellow given example where we are considering a layer architecture style.

The Presentation Layer(PL) which may contain User Interface Layer(UI) and Presentation Logic Layer(PLL).To make it easy say we have a web application and the UI means the web-form like *.aspx for ASP.NET or the view like *.cshtml for ASP.NET MVC. PLL means the controller class for the ASP.NET MVC or the *.aspx.cs for ASP.NET.

Business Layer (BL) could be you domain layer. The Data Access Layer (DAL). You can use any ORM like Entity-Frame-work as a DAL or whatever you want.

Say, you have bought some products from any web portal. Now you want to see your order items. So, when you click on a button to display your order then View (UI) will communicate with the controller class (PLL). Controller class will call the BL and then BL will call the DAL to verify the customer account information. Finally, DAL will send the order information to the BL and BL send it to the PL. So, you will see your order list.

In a short, the layers work as follows -

  1. PL depends on BL
  2. BL depends on DAL

So, high level layer depends on the low level layers. Say, these are tight coupled layers. So, we can't able to change this dependency at the run time or if we have more than one implementations of the DAL or BLL then what happen next? Can we able to change the tight-coupling of the dependency from the layers?

Solution from DI

It states that "High-level layers should not depend on low-level layers. Both should depend on abstractions".

So, we can inject any of the implementation of the DAL to the BL and any of the implementation of BL to the PL. Design plan is cool, huh!

See the class diagram, where we have implemented DataAccessForSQL, DataAccessForOracle classes from the interface IDataAccess. We have also implemented ProductBusinessLogic class from the interface IProductBusinessLogic.

Now we will inject either DataAccessForSQL or DataAccessForOracle into the ProductBusinessLogic via constrictor injection.

Finally, we have created dataAccess object via either DataAccessForSQL or DataAccessForOracle according to the input during run-time then the dataAccess object is injected into productBl object via constrictor injection. See the codes bellow -

OMG! You are adding more and more descriptions. Alright, I am stopping here. Even I will get time to review it. Let me know if you don't understand properly. I'm including the project and source codes with it. Find the attachment.

Remember one thing, these are all principles only, not hard coded rules like any holy books. So, relax and do your best then design will find its way home.

License

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

Share

About the Author

HR Rony
Engineer GIS
United States United States
Motivated software developer in web/mobile application development, architecture design, and multi-layer implementation. Well-versed in multiple programming languages and platforms. Demonstrated success in UI, integration, and automated unit testing.

You may also be interested in...

Pro
Pro

Comments and Discussions

 
PraiseGreat Article Pin
Far-way8-May-17 12:39
memberFar-way8-May-17 12:39 
QuestionIsn't this article kind of a copy? Pin
Paulo Zemek30-Apr-17 17:39
professionalPaulo Zemek30-Apr-17 17:39 
AnswerRe: Isn't this article kind of a copy? Pin
HR Rony2-May-17 5:56
memberHR Rony2-May-17 5:56 
AnswerRe: Isn't this article kind of a copy? Pin
KarstenK7-May-17 21:47
memberKarstenK7-May-17 21:47 
GeneralRe: Isn't this article kind of a copy? Pin
HR Rony10-May-17 12:49
memberHR Rony10-May-17 12:49 
AnswerRe: Isn't this article kind of a copy? Pin
pikougr8-May-17 2:38
memberpikougr8-May-17 2:38 
GeneralRe: Isn't this article kind of a copy? Pin
HR Rony10-May-17 12:49
memberHR Rony10-May-17 12:49 
QuestionLiskov Substitution Pin
Paulo Zemek30-Apr-17 16:45
professionalPaulo Zemek30-Apr-17 16:45 
AnswerRe: Liskov Substitution Pin
HR Rony2-May-17 5:55
memberHR Rony2-May-17 5:55 
Question[My vote of 2] Beautiful images and great concept but something is missing Pin
Mystcreater30-Apr-17 14:27
memberMystcreater30-Apr-17 14:27 
AnswerRe: [My vote of 2] Beautiful images and great concept but something is missing Pin
HR Rony2-May-17 5:25
memberHR Rony2-May-17 5:25 
SuggestionFeedback Interface Segregation Principle Pin
Jawad Munir25-Apr-17 7:08
memberJawad Munir25-Apr-17 7:08 
GeneralRe: Feedback Interface Segregation Principle Pin
HR Rony25-Apr-17 10:47
memberHR Rony25-Apr-17 10:47 
SuggestionIs a Client a baseAddress? Pin
Huh? Come Again?25-Apr-17 5:23
memberHuh? Come Again?25-Apr-17 5:23 
GeneralRe: Is a Client a baseAddress? Pin
HR Rony25-Apr-17 13:34
memberHR Rony25-Apr-17 13:34 
QuestionDuplication and BaseAddress Pin
Manuel A Lombardi T24-Apr-17 23:41
memberManuel A Lombardi T24-Apr-17 23:41 
AnswerRe: Duplication and BaseAddress Pin
Member 1232132925-Apr-17 8:46
memberMember 1232132925-Apr-17 8:46 
GeneralRe: Duplication and BaseAddress Pin
HR Rony25-Apr-17 13:48
memberHR Rony25-Apr-17 13:48 
AnswerRe: Duplication and BaseAddress Pin
HR Rony25-Apr-17 13:57
memberHR Rony25-Apr-17 13:57 
GeneralRe: Duplication and BaseAddress Pin
Manuel A Lombardi T26-Apr-17 1:41
memberManuel A Lombardi T26-Apr-17 1:41 
GeneralRe: Duplication and BaseAddress Pin
HR Rony27-Apr-17 3:31
memberHR Rony27-Apr-17 3:31 
QuestionLet's start with basics... Pin
PureNsanity24-Apr-17 4:50
professionalPureNsanity24-Apr-17 4:50 
AnswerRe: Let's start with basics... Pin
ktgw24-Apr-17 18:12
memberktgw24-Apr-17 18:12 
GeneralRe: Let's start with basics... Pin
Sharjith2-May-17 4:22
professionalSharjith2-May-17 4:22 
GeneralRe: Let's start with basics... Pin
HR Rony2-May-17 5:44
memberHR Rony2-May-17 5:44 
GeneralRe: Let's start with basics... Pin
Sharjith2-May-17 6:57
professionalSharjith2-May-17 6:57 
GeneralRe: Let's start with basics... Pin
HR Rony2-May-17 8:25
memberHR Rony2-May-17 8:25 
GeneralRe: Let's start with basics... Pin
HR Rony2-May-17 10:36
memberHR Rony2-May-17 10:36 
GeneralRe: Let's start with basics... Pin
Sharjith2-May-17 18:51
professionalSharjith2-May-17 18:51 
GeneralRe: Let's start with basics... Pin
HR Rony2-May-17 19:00
memberHR Rony2-May-17 19:00 
AnswerRe: Let's start with basics... Pin
HR Rony24-Apr-17 18:14
memberHR Rony24-Apr-17 18:14 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.171020.1 | Last Updated 24 Apr 2017
Article Copyright 2017 by HR Rony
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid