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.)

Open-Closed Principle

The Open-Closed Principle says that a class should be open for extension, but closed for modification. In other words, you should be able to easily change the behavior of the class in question without having to modify it.

The next time you are at a hardware store, look at the power tools. You will notice that there are a wide range of saw blades that can attach to a single saw. One blade compared to another may not look very different at first, but a closer inspection may reveal some significant differences. Some blades are constructed with different metals, the number of teeth or edges may vary, and the material that is used for the teeth is often designed for special purposes. No matter what the difference, though, if you are comparing two blades that attach to the same type of saw, they will have one thing in common: how they attach to the saw-the interface between the saw and the blade.

The individual differences of the blades are what make each type of blade unique. One blade may cut through wood extremely quickly, but leave the edges rough. Another blade may cut wood more slowly and leave the edges smooth. Still others may be suited for cutting metal or other materials. The wide variety of blades, combined with the common method of attaching them to the saw, allows you to change the behavior of the saw without having to modify the mechanical portion of the saw.

So, how do you allow a class’s behavior to be modified without actually modifying the class? The answer is surprisingly simple and there are several methods for doing this.

Have you ever implemented an interface in a class and then passed an instance of that class into another object? Perhaps you implemented the IPrincipal interface for custom security needs. Or, you may have written your own interface such as the classic example of IAnimal, and implemented a Cat and a Dog object from this interface. The ubiquitous nature of explicit interfaces in .NET, as well as abstract base classes, delegates, and other forms of abstraction, all provide different ways of allowing custom behavior to be supplied to existing classes and modules. You can use design patterns such as Strategy, Template, State, and others to facilitate the behavioral changes through the use of such abstractions. There are still other patterns and abstractions, and other methods of injecting behavior and altering the class at runtime. Chances are, if you have written an application that required even a small amount of flexibility, you have either provided a custom behavior implementation to an existing class, or have written a class that required a custom behavior to be supplied.

Restructuring for Open-Closed

Given the need for multiple, unknown file types to be parsed, you decide to supply an interface that can be implemented by any number of objects, from any number of third parties, including the network operations personnel. In addition to the actual file parsing, you will need the interface to tell you whether or not the specific implementation can handle the current file contents. Your resulting application structure looks more like Figure 7, with the IFileFormatReader interface defined as follows:

Click for a larger version of this image.

Figure 7: Enabling open/closed via IFileFormatReader.

public interface IFileFormatReader
{
   bool CanHandle(string fileContents);
   string GetMessageBody(string fileContents);
}

Since you know that there are multiple file formats being read now, you also decide to move the existing code that reads the flat file and XML file formats into two separate objects. The flat file reader can handle any non-binary log file, so you decide that this handler does not need to determine if it can handle the file contents sent to it. It only needs to say that it can handle the format, and then send the original content back out. You rewrite the implementation of the flat file format reader as follows:

class FlatFileFormatReader: IFileFormatReader
{
  public bool CanHandle(string fileContents)
  {
    return true;
  }

  public string GetMessageBody(
    string fileContents)
  {
    return fileContents;
  }
}

The XML file format reader will contain a check to see if the XML is valid. The GetMessageBody method will then parse the XML for the content, as shown in Listing 3.

Next, you want to introduce the FileReaderService class. This will use the various IFileFormatReader implementations and is where the behavioral change will occur when the various format readers are supplied.

To support an unknown number of file format readers, you decide to store the list of registered format readers in a simple collection:

IList<IFileFormatReader_formatReaders = new 
  List<IFileFormatReader>();

public void RegisterFormatReader(
  IFileFormatReader fileFormatReader)
{
  _formatReaders.Add(fileFormatReader);
}

The RegisterFormatReader method allows any code that calls the FileReaderService API to register as many format readers as they need. Then, when a file needs to be parsed, a call to a GetMessageBody method is made, passing in the contents of the file as a string. This method runs through the list of registered format readers and checks to see if the current one can handle the format. If it can, it calls the GetMessageBody method of the reader and returns the data.

public string GetMessageBody(string fileContents)
{
  string messageBody = string.Empty;
  foreach(IFileFormatReader formatReader 
    in _formatReaders)
  {
    if (formatReader.CanHandle(fileContents))
    {
      messageBody = formatReader
       .GetMessageBody(fileContents);
      break;
    }
  }
  return messageBody;
}

At this point, if there is no registered reader that can handle the file contents, an empty string is returned. You realize that you need to add a default file reader. The intention is to ensure that all log files are handled, regardless of the content. If a file can’t be handled by any other reader, you will want to return all of the content through the flat file format reader.

By adding a separate RegisterDefaultFileReader method, you can ensure that only one default exists. Listing 4 shows the resulting GetMessageBody implementation.

Finally, you need to update the usage of the FormatReader object in your EmailSender. You need to register both the XML file format reader and the flat file format reader in the constructor of the email sender class.

private readonly FileReaderService   
  _fileReaderService = new FileReaderService();

public EmailSender()
{
  _fileReaderService.RegisterFormatReader(
    new XmlFormatReader());
  _fileReaderService.
    RegisterDefaultFormatReader(
      new FlatFileFormatReader());
}

Happy Consumers and More Requirements

A few days after releasing this version of the application and API, you hear that the operations group loves your IFileFormatReader and the extensibility it brings to the table. They have successfully implemented several format readers and are planning on more.

A short time later, a new request comes in that you were not expecting. One system the operations group must support logs all of its errors to a database, not a text file. Moreover, according the operations personnel, they cannot write code that hits the database in question. Apparently, that’s “above their pay grade.” They need someone on the development staff to do it, and are asking for your help.

The most challenging part of this new requirement is the CTO being involved, again. Due to the high visibility of this project and the potential for lost revenue if errors are not proactively corrected, he wants your application updated to support reading from the database, immediately. According to your manager, when the CTO says “immediately” he usually means before the end of the day. It’s only a few hours before the day ends and you’ve been running on very little sleep for the last few days, but you think you can bang out a working version and get it to the operations group in time to make the CTO happy.

&


Liskov Substitution

If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.

-Barbara Liskov



Listing 3: The new XMLFileFormatReader object
public class XmlFileFormatReader: IFileFormatReader
{
  XmlDocument _xmlDoc;

  public bool CanHandle(string fileContents)
  {
    bool canHandle;
    try
    {
      _xmlDoc = new XmlDocument();
      _xmlDoc.LoadXml(fileContents);
      canHandle = true;
    }
    catch (Exception)
    {
      canHandle = false;
    }
    return canHandle;
  }

  public string GetMessageBody(string fileContents)
  {
    string messageBody = _xmlDoc.SelectSingleNode("//email/body")
      .InnerText;
    return messageBody;
  }
}


Listing 4: GetMessageBody with default file format reader
private IFileFormatReader _defaultFormatReader;
public void RegisterDefaultFormatReader(
  IFileFormatReader defaultFormatReader)
{
  _defaultFormatReader = defaultFormatReader;
}

public string GetMessageBody(string fileContents)
{
  string messageBody = string.Empty;
  bool wasHandled = false;
  
  foreach(IFileFormatReader formatReader in _formatReaders)
  {
    if (formatReader.CanHandle(fileContents))
    {
      messageBody = formatReader.GetMessageBody(fileContents);
      wasHandled = true;
    }
  }

  if (!wasHandled)
  {
    messageBody = _defaultFormatReader
      .GetMessageBody(fileContents);
  }

  return messageBody;
}


Article Pages: < Previous - 1 2 3 4  5  6 7 8 - Next Page: 'Liskov Substitution 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
 

      LearnNow

 

SSWUG