Thursday, 18 May 2006
Just because an application is developed in .NET doesn't make it object oriented.

It might have classes.

It might have interfaces.

It might have methods.

It still is a mass of spaghetti code, with some objects thrown in to act as meatballs.

It never ceases to amaze me what lengths developers will go to to turn .NET into a overwrought scripting language, instead of harnessing some simple OOP principles to make their code and their lives simpler.

So, today we are going to talk about Encapsulation. Yes, I have spent the last two weeks ripping through an app that had objects, but frequently didn't use them as much more than fancy structures.  And, yes, I will rant a little bit (more).

Encapsulation is probably the simplest OOP concept to grasp, and it should be the easiest to implement.  A developer does not need to understand class factories, inheritance or polymorphism to develop encapsulated classes.  A desire for clean interfaces coupled with an abhorrence for writing the same code twice will lead naturally to encapsulation.

Yet, this well-known tool of code reuse, is often left unloved and dusty, next to the once-cracked Gang of Four OOP bible.

I like to think of Encapsulation as empowering an object.  It allows the object to say, "This is what I do. This is what I expect.  Don't tell me how to do my job, just give me what I need and let me do it."

Simple, no?  You'd think so, but it is clearly not a universal practice.

Here is an object.  What it does really doesn't matter for this discussion.  It has two ways of being populated -- one when it is first created, another for when it is  reconstituted from the database. It has two consctructors. This is a very common situation. 

public class SomeEntity{

    //Constructor for new instance
     public SomeEntity(string Name, string ANeededValue, int AnotherNeededValue){...}
    
    //Constructor for a retrieved instance
     public SomeEntity(int ID, string Name, XmlDocument TheReasonForTheObject){...}

    //Members
    private string mName;
    //...

    //Properties, Methods, Etc.

    public void DoSomethingImportant(){...}

    public string Name{
       { get{return mName;}
    }
    //...
}

Now, I don't have a problem with the first constructor -- at least there is one, this actually demonstrates an important bit of encapsulation: controlling how the object is instantiated.  The constructor gives a way to tell the world,  "This is what I need to start properly!"

For the free-for-all that can result without encapsulated instantiation, here's the same object sans a defined constructor...

public class SomeEntity{

       //default constructor
       public SomeEntity(){}
  
    //Members
    public string Name;
    public string ANeededValue;
    public int AnotherNeededValue;
   
   
//Properties, Methods, Etc....
    public void DoSomethingImportant(){...}

}

What's required to make sure an instance of this object works properly?  The developer's dilligence, memory and typing skills.  Everywhere the object is created, the developer must remember to type four lines of code -- just to get the object in a state where it is minimally functional.

SomeEntity AnObjectINeed = new SomeEntity();
AnObjectINeed.Name = "Fred";
AnObjectINeed.ANeededValue = "Tuesday";
AnObjectINeed.AnotherNeededValue = 789;

Forget to supply a value -- oops, error. What's missing?  Hope the exception message is clear and go hunting. It is bad enough that this code will be cut-and-pasted willy-nilly every time the object is needed, but what is worse is the maintenance implication.  Things change, the object needs an additonal value to work properly....

public class SomeEntity{

  
    //Members
    public string Name;
    public string ANeededValue;
    public int AnotherNeededValue;
    public DateTime OoopsForgotThis;
   
    //Properties, Methods, Etc....
    public void DoSomethingImportant(){...}

}

Hmmm, what happens now? The object won't work without the new value, but the application still compiles.  The developer must hunt for EVERY creation of the object and add another line of code, and then clean up the inevitable bugs when he misses a few.

Using a defined constructor avoids this problem.

    //Constructor for new instance
     public SomeEntity(string Name, string ANeededValue, int AnotherNeededValue, DateTime OoopsForgotThis){...}

The application won't compile now. All the places the code needs to be changed will be listed, they don't have to be hunted for.  Once the changes have been made and the app compiles, the object will always have what it needs to do its job.

Pardon the long aside, and let me finally return to what bothered me about the second constructor in the example above...

//Constructor for a retrieved instance
  public SomeEntity(int ID, string Name, XmlDocument TheReasonForTheObject){...}

What bothers me here is a subtle, but more troubling, lack of encapsulation related to this constructor. 

In order to call this constructor, three parameters must be supplied, an ID, a name and an XMLDocument. Now, where are these values stored? In a database record.  They are retrieved via a stored procedure call using the entity's ID, which is a unique integer value. 

A UNIQUE value.

The ID is unique and possesses all the object needs to know to reconstitute itself.  Why then, are the other two values needed in the constructor?

They're NOT!

Why are they there? 

I have no friggin' clue.  But this is part of the foo I've been fighting for the last few weeks. There are 5 or 6 lines of database-related code that precede every use of this constructor, along with the creation of an XmlDocument object from its string respresentation.  That's 8 lines of code repeated each time the object is recreated from the database, all of which SHOULD have been placed in the object, so it could have populated itself.

But that's not all?  What is controlling whether or not:
1) The name and XML document supplied are actually related to the supplied ID?
2) The XMLDocument has the proper schema the object expects?

The memory, diligence and typing skills of the developer.  In other words, NO ONE! 

By writing the object so it controls its own data retrieval, these problems are avoided. The object saved (or should have saved -- don't get me started) its own data and can be fairly confident it is getting the same information back -- in proper form. 

//Constructor for a retrieved instance
public SomeEntity(int ID){...}
The object is empowered, it controls its data, it works properly.  It can be used in code without requiring 8 supporting lines of code to be written each time. 

Why? 

It's encapsulated!

Want to make the app work when its disconnected and need to store the object in a local Access database?  No problem, change the storage and retrieval code in the object.  The rest of the app doesn't know and doesn't care how or where the object data is stored.  All it knows and cares about is that if it saved a SomeEntity object with a unique ID = 1776, that when this code is called...

    SomeEntity AnObjectINeed = new SomeEntity(1776);

It will get back a SomeEntity object with an ID = 1776 and the proper XMLDocument.

An object born ready to do what it is supposed to do.

What's hard about that?

Really?

Thursday, 18 May 2006 22:35:28 (Eastern Standard Time, UTC-05:00)   #     Comments [0]  | 
Wednesday, 15 March 2006

Among the many improvements of .NET 2.0 is a subtle but welcome one in GridView, the replacement to the DataGrid control.

The Items have finally left the building.  Any old kinda grid control should have columns and rows.

The DataGrid had Columns and Items?

What in the world is an Item?  It's a row, but it's not called that -- for a very good reason I am sure.

The GridView has Columns and Rows!

Hooray!

Instead of ItemCreated, or ItemDataBound event handlers, there are RowCreated and RowDataBound.  Instead of ItemType there is RowType.

I cannot tell you how confusing this was for developers when first working with the DataGrid.  "How can I access a row?" must have been screamed at monitors from Mountain View to Mumbay.

What things are named can greatly affect how easy/hard it is use someone else's interface or maintain code.  How many times have you written code for something, only to find the same functionality in an obscurely/badly named function or class?

Consistent, meaningful naming makes it much easier for someone else, or the future you (go read some code you haven't touched in a year) to get what you are trying to do.

I must confess that I have not always been the best practioner.  My original programming platform was the Apple II, and variable names were limited to 8 characters (only the first three of which were meaningful -- found that out the HARD way), and I've never fully overcome the combined effects of that and my Modified Hunt & Peck Typing skills. 

This is something I have worked to be better at, but this post from Ryan Olshan really made it hit home.  I will paraphrase his thesis as "Variable names should be like characters in novel, they should be easily identifiable and make the reader care what happens to them". 

Especially with Intellisense and the built-in refactoring tools in Visual Studio (this one especially), there is no reason to skimp on meaningful naming and it's easy to fix past mistakes.

Wednesday, 15 March 2006 12:41:54 (Eastern Standard Time, UTC-05:00)   #     Comments [0]  | 

Theme design by Dean Fiala

Pick a theme: