Introduce DataBase,Asp.net,JavaScript,Xml,Html,Css,Sql,Php,ASP.NET Controls,AJAX,Tools,HTML,CSS,JavaScript,Open Source Project,WPF,.Net Framework,Linq
Top Recommended Hosting

Limit Visibility of Your Types

by the3factory 5/5/2008 8:10:00 AM

Not everybody needs to see everything. Not every type you create needs to be public. You should give each type the least visibility necessary to accomplish your purpose. That's often less visibility than you think. Internal or private classes can implement public interfaces. All clients can access the functionality defined in the public interfaces declared in a private type.

Let's get right to the root cause: powerful tools and lazy developers. VS .NET is a great productivity tool. I use it or C# Builder for all my development simply because I get more done faster. One of the productivity enhancements lets you create a new class with two button clicks. If only it created exactly what I wanted. The class that VS.NET creates looks like this:

public class Class2
{
public Class2()
{
//
// TODO: Add constructor logic here
//
}
}

It's a public class. It's visible to every piece of code that uses the assembly I'm creating. That's usually too much visibility. Many standalone classes that you create should be internal. You can further limit visibility by creating protected or private classes nested inside your original class. The less visibility there is, the less the entire system changes when you make updates later. The fewer places that can access a piece of code, the fewer places you must change when you modify it.

Expose only what needs to be exposed. Try implementing public interfaces with less visible classes. You'll find examples using the Enumerator pattern throughout the .NET Framework library. System.ArrayList contains a private class, ArrayListEnumerator, that implements the IEnumerator interface:

// Example, not complete source
public class ArrayList: IEnumerable
{
private class ArraylistEnumerator : IEnumerator
{
// Contains specific implementation of
// MoveNext( ), Reset( ), and Current.
}
public IEnumerator GetEnumerator()
{
return new ArrayListEnumerator( this );
}
// other ArrayList members.
}

Client code, written by you, never needs to know about the class ArrayListEnumerator. All you need to know is that you get an object that implements the IEnumerator interface when you call the GetEnumerator function on an ArrayList object. The specific type is an implementation detail. The .NET Framework designers followed this same pattern with the other collection classes: Hashtable contains a private HashtableEnumerator, Queue contains a QueueEnumerator, and so on. The enumerator class being private gives many advantages. First, the ArrayList class can completely replace the type implementing IEnumerator, and you'd be none the wiser. Nothing breaks. Also, the enumerator class need not be CLS compliant. It's not public (see Item 30.) Its public interface is compliant. You can use the enumerator without detailed knowledge about the class that implements it.

Creating internal classes is an often overlooked method of limiting the scope of types. By default, most programmers create public classes all the time, without any thought to the alternatives. It's that VS .NET wizard thing. Instead of unthinkingly accepting the default, you should give careful thought to where your new type will be used. Is it useful to all clients, or is it primarily used internally in this one assembly?

Exposing your functionality using interfaces enables you to more easily create internal classes without limiting their usefulness outside of the assembly (see Item 19). Does the type need to be public, or is an aggregation of interfaces a better way to describe its functionality? Internal classes allow you to replace the class with a different version, as long as it implements the same interfaces. As an example, consider a class that validates phone numbers:

public class PhoneValidator
{
public bool ValidateNumber( PhoneNumber ph )
{
// perform validation.
// Check for valid area code, exchange.
return true;
}
}

Months pass, and this class works fine. Then you get a request to handle international phone numbers. The previous PhoneValidator fails. It was codedto handle only U.S. phone numbers. You still need the U.S. Phone Validator, but now you need to use an international version in one installation. Rather than stick the extra functionality in this one class, you're better off reducing the coupling between the different items. You create an interface to validate any phone number:

public interface IPhoneValidator
{
bool ValidateNumber( PhoneNumber ph );
}

Next, change the existing phone validator to implement that interface, and make it an internal class:

internal class USPhoneValidator : IPhoneValidator
{
public bool ValidateNumber( PhoneNumber ph )
{
// perform validation.
// Check for valid area code, exchange.
return true;
}
}

Finally, you can create a class for international phone validators:

internal class InternationalPhoneValidator : IPhoneValidator
{
public bool ValidateNumber( PhoneNumber ph )
{
// perform validation.
// Check international code.
// Check specific phone number rules.
return true;
}
}

To finish this implementation, you need to create the proper class based on the type of the phone number. You can use the factory pattern for this purpose. Outside the assembly, only the interface is visible. The classes, which are specific for different regions in the world, are visible only inside the assembly. You can add different validation classes for different regions without disturbing any other assemblies in the system. By limiting the scope of the classes, you have limited the code you need to change to update and extend the entire system.

You could also create a public abstract base class for PhoneValidator, which could contain common implementation algorithms. The consumers could access the public functionality through the accessible base class. In this example, I prefer the implementation using public interfaces because there is little, if any, shared functionality. Other uses would be better served with public abstract base classes. Either way you implement it, fewer classes are publicly accessible.

Those classes and interfaces that you expose publicly to the outside world are your contract: You must live up to them. The more cluttered that interface is, the more constrained your future direction is. The fewer public types you expose, the more options you have to extend and modify any implementation in the future.

Related posts

Sign up for PayPal and start accepting credit card payments instantly.


Powered by BlogEngine.NET 1.2.0.0