Content by Category
.NET 1.x
.NET 2.0
.NET 3.0
.NET 3.5
.NET 4.0
.NET 4.5
.NET Assemblies
.NET Framework
.NET Getting Started
Accessibility
ADO.NET
Advertorials
Agile Development
AJAX
Amazon Web Services
Analysis Services
Android
Architecture
Arduino
ASP .NET Web API
ASP.NET
ASP.NET MVC
ASP.NET WebForms
Azure
B2B (Business Integration)
BDD
Big Data
Bing
BizTalk
Book Excerpts
Build and Deploy
Business Intelligence
C#
C++
ClickOnce
Cloud Computing
Code Contracts
CODE Framework Info - non Technical
CODE on the Road!
COM+
Community
Conferences
Continuous Integration
Crystal Reports
CSLA.NET
CSS
Data
Debugger
Design Patterns
Development Process
Display Technologies
Distributed Computing
Document Database
DotNetNuke
DSL
Dynamic Languages
Dynamic Programming
Editorials
Enterprise Services ("COM+")
Entity Framework
Events
Expression Blend
F#
Fox to Fox
Frameworks
Functional Programming
Git
Graphics
HTML 5
Internet Explorer 8.0
Interviews
IOS
iPhone
Iron Ruby
Java
Java Script
JavaScript
jQuery
JSON
Lightswitch
LINQ
Linux
LUA
Mac OS X
MDX
Messaging
Metro
Microsoft Application Blocks
Microsoft Business Rules Framework
Microsoft Dynamics
Microsoft Expression
Microsoft Office
Mobile Development
Mobile PC
Mono
MsBuild
MVVM
MySQL
Network
NHibernate
node.js
NOSQL
Nuget
Object Oriented Development
Objective C
Odata
OLAP
Open Source
Opinion
Opinions
Oracle
ORM
Other Languages
Parallel Programming
Patterns
PHP
Podcasts
Post Mortem
PowerPoint
Print/Output
Prism
Product News
Product Reviews
Project Management
Prolog
Python
Q&A
Rails
Rake
Razor
Reporting Services
REST
RIA Services
Ruby
Ruby on Rails
Scheme
Search
Security
Services
SharePoint
SignalR
Silverlight
SOA
Social Networks
Software & Law
Software Business
Source Control
Speech-Enabled Applications
SQL Server
SQL Server 2000
SQL Server 2005
SQL Server 2008
SQL Server 2012
SQL Server CE/AnyWhere/Mobile/Compact
SSIS
Subversion
Sync Framework
Tablet PC
TDD
Team System
Techniques
Testing and Quality Control
TFS
Tips
TypeScript
UI Design
UML
User Groups
VB Script
VB.NET
Version Control
VFP and .NET
VFP and SQL Server
Virtual Earth
Vista
Visual Basic
Visual Basic 6 (and older)
Visual FoxPro
Visual Studio .NET
Visual Studio 11
Visual Studio 2005
Visual Studio 2008
Visual Studio 2010
Visual Studio 2011
Visual Studio 2012
Visual Studio Tools for Office
VSX
WCF
Web Development (general)
Web Services
WebMatrix
WF
Whitepapers
Windows 7
Windows 8
Windows Azure
Windows Live
Windows Phone 7
Windows Phone SDK
Windows Server
Windows Vista
WinForms
WinRT
Workflow
WPF
XAML
Xiine Documentation
XML
XNA
XSLT



LearnNow


XAMALOT
 


SSWUG

Reader rating:
Click here to read 19 comments about this article.
Article source: CoDe (2010 Jan/Feb)


Article Pages: < Previous - 1 2 3 4 5  6  7 8 - Next >


S.O.L.I.D. Software Development, One Step at a Time (Cont.)

Liskov Substitution Principle

The Liskov Substitution Principle says that an object inheriting from a base class, interface, or other abstraction must be semantically substitutable for the original abstraction. Even if the original abstraction is poorly named, the intent of that abstraction should not be changed by the specific implementations. This requires a solid understanding of the context in which the interface was meant to be used.

To illustrate what a semantic violation may look like in code, consider a square and a rectangle, as shown in Figure 8. If you are concerned with calculating the area of a resulting rectangle, you will need a height, a width and an area method that returns the resulting calculation.

Click for a larger version of this image.

Figure 8: Semantic violations are not always easy to see.

public class Rectangle
{
  public virtual int Height { getset; }
  public virtual int Width { getset; }

  public int Area()
  {
    return Height * Width;
  }
}

In geometry, you know that all squares are rectangles. You also know that not all rectangles are squares. Since a square “is a” rectangle, though, it seems intuitive that you could create a rectangle base class and have square inherit from that. But what happens when you try to change the height or width of a square? The height and width must be the same or you no longer have a square. If you try to inherit from rectangle to create a square, you end up changing the semantics of height and width to account for this.

public class Square : Rectangle {  
  public override int Height {
    get return base.Height; }
    set base.Height = value;
          base.Width = value;
    }
  }
  public override int Width {
    get return base.Width; }
    set base.Width = value;
          base.Height = value;
    }
  }
}

What happens when you use a rectangle base class and assert the area of that rectangle? If you expect the rectangle’s area to be 20, you can set the rectangle’s height to 5 and width to 4. This will give you the result you expect.

Rectangle rectangle = new Rectangle();
rectangle.Height = 4;
rectangle.Width = 5;
AssertTheArea(rectangle);

private void AssertTheArea(Rectangle rectangle)
{
  int expectedArea = 20;
  int actualArea = rectangle.Area();
  Debug.Assert(expectedArea == actualArea);
}

What if you decide to pass a square into the AssertTheArea method, though? The method expects to find an area of 20. Let’s try to set the square’s height to 5. You know that this will also set the square’s width to 5. When you pass that square into the method, what happens?

Rectangle square = new Square();
square.Height = 5;
AssertTheArea(square);

private void AssertTheArea(Rectangle rectangle)
{
  int expectedArea = 20;
  int actualArea = rectangle.Area();
  Debug.Assert(expectedArea == actualArea);
}

You get the wrong result because 5 x 5 is 25, not 20. That is too high, so now try a height of 4 instead. You know that 4 x 4 is 16. Unfortunately, that’s too low. So the question is, “how can you get 20 out of multiplying two integers?” The answer is: you can’t.

The square-rectangle issue illustrates a violation of the Liskov Substitution Principle. You clearly have the wrong abstraction to represent both a square and a rectangle for this scenario. This is evidenced by the square overriding the height and width properties of the rectangle, and changing the expected behavior of a rectangle.

What, then, would a correct abstraction be? In this case, you may want to use a simple Shape abstraction and only provide an Area method, as shown in Listing 5. Each specific implementation-square and rectangle-would then provide their own data and implementation for area allowing you to create additional shapes such as circles, triangles, and others that you don’t yet need. By limiting the abstraction to only what is common among all of the shapes, and ensuring that no shape has a different meaning for “area” you can help prevent LSP violations.

A Quick-and-Dirty Database Reader

After fuelling up with another energy drink and shaking off the sleep you so desperately want, you dive into the code for the database reader. Given the short time frame, you decide to take a shortcut and not introduce a new abstraction or a new method to the API. Rather, you decide to hard code the behavior of reading from the database into the application, facilitated by the use of a special file format reader. You know it’s not the brightest moment in your career, but you just want to get it out the door and go home for the night. What should have been an 800-meter race has now become a 50-meter dash in your mind. Listing 6 shows the result.

You deliver the working code on time, and manage to make it home before falling asleep while driving. Overall, you consider it to be a successful day.

The following week, you hear word that the network operations personnel liked the ability to read from a database. In fact, they liked it so much that they told another department about this feature. What they didn’t know, though, was that the code you delivered was written for one very specific database and didn’t actually read the connection string from a file. You knew that the security guys would have your head if you stored the real connection information in a plain text file, so you hard coded it into the file reader service. You created the “server=” content of the file as a placeholder to let you know that you should use the database connection reader.

So, when the network operations personnel gave your code to the other department, everyone started wondering why the other department was now reading log files from the network operations center. All eyes are now looking squarely at you.

Revisiting the Database Reader

All of the eyes looking squarely at you were from your friends in the company, fortunately. After explaining the stress and sleeplessness that undermined your ability to code that day, they all laughed and asked when you would have a new version ready for them. Remembering that sprinting out of the gate during a marathon race is likely to cause the same problems again, you inform them that you’ll need a day or two to get the situation sorted out correctly. There’s no immediate need or CTO putting on the pressure at this point, so everyone agrees to the general timeline and waits patiently while you work.

After a quick discussion with some coworkers, you realize that you had changed the semantics of the file format reader interface and introduced behavior that was incompatible. After a little more discussion, you end up with the design represented by Figure 9, and the change turns out to be fairly simple.

Click for a larger version of this image.

Figure 9: Restructuring database reading to correct the LSP violation.

"
By introducing a separate database reader service, you can remove the type-checking code from the file reader service.
"

By introducing a separate database reader service, you can remove the type-checking code from the file reader service. You can set up the database reader to read the required connection string from the company standard storage for sensitive data. That decision makes the people in network operations, security, and the other department that wants to use the code, happy.

Next, you update the UI to include a “Send From Database” button as shown in Figure 10. This button calls into the same email sender object that you’ve been using as the public API. However, the email sender now has a ReadFromDatabase method along with a ReadFromFile method. This keeps the public API centralized while still providing the functionality that the various departments need.

Click for a larger version of this image.

Figure 10: The UI updated with the “Send From Database” functionality.

public class EmailSender
{
  public void ReadFile() 
  { /* ... */ }

  public void SendEmail() 
  { /* ... */ }

  public void ReadDatabase() 
  { /* ... */ }
}

With this newly structured system in place, you deliver the solution to both of the waiting departments. Your friends are happy to hear that you’ve been getting more sleep and that the application they’ve been waiting for is “finally done” -a day earlier than promised.

Still More Use for Your Application

Shortly after delivering the updated version of the application with the database reading capabilities, another department gets wind of it and they want to use the API. After a quick conversation with them to find out if your application is what they really need, you deliver the working bits. A day later, one of the developers from that department stops by your cube with a confused look on his face. After some quick chat, you realize that he’s confused by the email sender object. It seems that he doesn’t understand why there’s a “read from database” and “read from file” method on an object that is supposed to send email.

&


Interface Segregation

This principle deals with the disadvantages of fat interfaces. Classes that have fat interfaces are classes whose interfaces are not cohesive. In other words, the interfaces of the class can be broken up into groups of member functions. Each group serves a different set of clients. Thus some clients use one group of member functions, and other clients use the other groups.

-Robert Martin



Listing 5: A correct abstraction for a square and rectangle
public interface Shape
{
  int Area();
}

public class Rectangle : Shape
{
  public virtual int Height { getset; }
  public virtual int Width { getset; }

  public int Area() { return Height * Width; }
}

public class Square : Shape
{
  public int LengthOfSides { getset; }

  public int Area() { return LengthOfSides ^ 2; }
}

public class DoStuff
{
  public void DoTheRectangleStuff()
  {
    Rectangle rectangle = new Rectangle();
    rectangle.Height = 4;
    rectangle.Width = 5;
    AssertTheArea(rectangle, 20);
  }

  public void DoTheSquareStuff()
  {
    Square square = new Square();
    square.LengthOfSides = 4;
    AssertTheArea(square, 16);
  }

  private void AssertTheArea(Shape shape, int expectedArea)
  {
    int actualArea = shape.Area();
    Debug.Assert(expectedArea == actualArea);
  }
}


Listing 6: Violating LSP with specific type checks
public class DatabaseConnectionReader : IFileFormatReader
{
  public bool CanHandle(string fileContents)
  {
    bool canHandle = false;
    if (fileContents.StartsWith("server="))
      canHandle = true;
    return canHandle;
  }
  public string GetMessageBody(string fileContents)
  {
    throw new NotImplementedException("Need to read from the 
      database! Not from this interface!");
  }
}

public class FileReaderService
{
  // ...
  public string GetMessageBody(string fileContents)
  {
    // ...
    foreach(IFileFormatReader formatReader in _formatReaders)
    {
      // ...
      if (formatReader.GetType()
          .Equals(typeof(DatabaseConnectionReader)))
      {
        messageBody = // GET THE LOG INFO FROM A DATABASE, HERE.
      }
      else
      {
        messageBody = formatReader.GetMessageBody(fileContents);
      }
      // ...
    }
    // ...
  }
  // ...
}


Article Pages: < Previous - 1 2 3 4 5  6  7 8 - Next Page: 'Interface Segregation Principle' >>

Page 1: S.O.L.I.D. Software Development, One Step at a Time
Page 2: High Cohesion
Page 3: S.O.L.I.D. Stepping Stones
Page 4: Single Responsibility Principle
Page 5: Open-Closed Principle
Page 6: Liskov Substitution Principle
Page 7: Interface Segregation Principle
Page 8: Decoupling and Inverting the Email Sending System Dependencies

How would you rate the quality of this article?
1 2 3 4 5
Poor      Outstanding

Tell us why you rated the content this way. (optional)

Average rating:
4.6 out of 5

93 people have rated this article.

Instantly Search Terabytes Of Text
“Lightning Fast”
– Redmond Mag
“Covers all data
sources” – eWeek
25+ fielded & full-text search options
dtSearch’s own document filters highlight hits in popular file types
Web Spider supports static & dynamic data
APIs for .NET, Java, C++, SQL, etc.
Win / Linux (64-bit & 32-bit)
www.dtSearch.com
 

      Sharepoint TechCon

 

SSWUG