共用方式為


Extention Methods and the Principal of Single Responsibility

Recently I was handed a solution I needed to do some refactoring on, everything was good except their user data was now coming from a different source. Originally, they were pulling it down from active directory and then putting most of the fields into a new class called User. Okay, good. Now, they were going to be getting user data out of a database and the fields weren’t exactly the same. Also, there was a change they were going to be getting the occasional user data from an old Oracle database as well. Don’t ask. Either way, I now had three classes with basically the same information. Here were some of the field differences.

SSN/SocialSecirityNumber/Social
Username/username/UserName
Birthdate/DOB/dateOfBirth (all in different date formats)

Ugh.

The application itself was used to this ‘User’ object, and was basically programmed to use those fields. When it wanted to get the SSN, it would use objectname.SSN, and when it wanted to calculate the age of the user it used objectname.Birthdate in the equation. Now there are a variety of ways we can integrate the new data sources into the application.

One of the ways is to go into each class, and to add another field where the getter/setter pulls the other field. For example if I go into the class that pulls the Oracle data…

 public string Social { get; set; }

public string SSN
{
   get { return this.Social;  }
   set { this.Social = value; }
}

 And then do this for all the different Classes. This is not the way you should do it. The class that gets the values from the database/active directory/wherever is doing its job of getting those values. They’re probably named like that in the database. Adding more fields or (the other thing that you thought of doing) adding a conversion method would break the Principal of Single Responsibility. This class is getting data from the source.

So we likely need a separate class that represents the data, and moves it around two whichever object needs it. A Data Transfer Object if you will. Since the application is designed to accept fields from the original Active Directory store, we can match those fields.

 public class UserDTO
{
    public string Name { get; set; }
    public string SSN { get; set; }
    public DateTime DOB { get; set; }
    /* more fields... */
}

 Now, we can change the application to use this object instead of the old User class it was used to. That modification isn’t that time consuming. So now we have a new UserDTO object, and we feel better because it doesn’t rely on the old User object that was based on Active Directory. Now we need to get the data from the data sources into the DTO. One of the things we could do is create conversion methods in the DTO. While this would work, it again violates the principal that a class should be only doing one thing. Plus, if we did that, this is how we would use it.

 public void DoStuff()
{
    DatabaseUser dbUser = new DatabaseUser();
    UserDTO user = UserDTO.ConvertFromDatabase(dbUser);
}

Not bad… notice how we have a DatabaseUser object (assume it came with data), then we created a UserDTO object and ran the conversion method to give us a NEW UserDTO object with values.

There’re really not anything wrong with this method, except it will require us to write conversion methods for the other types. What if we wanted to convert from a UserDTO to its old type? What if we needed this DTO in another part of the solution that doesn’t use/need/care about the old types? This DTO sure will carry a lot of baggage…

ENTER THE EXTENSION METHOD

So Extension Methods are basically static methods that attach themselves onto a parent class, without actually being in the parent class. Or as MSDN explains it: “Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type.” That’s our winner right there. Let’s create an extension method to handle the conversion…

 namespace Extensions
{
    public static class UserExtensions

    {
        public static SomeSolution.UserDTO ToUserDTO(this SomeSolution.DatabaseUser dbUser)
        {
            return new SomeSolution.UserDTO { Name = dbUser.Name, SSN = dbUser.SocialSecurityNumber, DOB = DateTime.Parse(dbUser.DateOfBirth) };
        }
    }
}

We plopped this in a new namespace, but you don’t necessarily need to do that. Also, the method is a public static method. Okay. So it looks like the conversion method that we had before. So… here’s the cool part.

 

Ah-ha! There is our extension method there! The conversion is done in a whole ‘nother class where we can manage it outside of the original class. Perfect.

Also, cool? Extension methods can extend the functionality of the String class. Remember, the String class is ‘sealed’ meaning it can’t be derived from (subclassed) but with Extension methods, we can add some extra functionality to strings! So look what I can do with our SSN.. 

 public static System.String ToSNFormat(this string ssn)
{
    return String.Format("{0}-{1}-{2}", ssn.Substring(0, 3), ssn.Substring(3, 2), ssn.Substring(5, 4));
}

Now, when we go and look at the options for our SSN field…

 

Boo-ya!