简体   繁体   中英

what's the name of this design pattern? (if it is one)

I have a large class, let's call it 'Country'. It has all kinds of properties like List States, Age, etc.

In real life, I am working on an ASP.NET application in which a user enrolls into a subscription for a service our client provides. This application form they need to fill out has many many properties and one of the main classes representing the data model is becoming too bloated, and so I want to break it into small classes via composition but still have properties on the subclasses linked to the parent class.

For this example to keep things simple and refer to the car example. So we have a class called Country that looks like this:

public static class WorldDatabase
{
    public static List<Country> Countries {get;set;}
}

class Country
{
    public int Age {get;set;}
    public string Name{get;set;}
    public List<State> States{get;set;}
}

class State
{
    public string CountryName{get;set;}
    public string Capitol{get;set;}
    public List<string> Cities{get;set;}
}

Now, creating a quick sample of the setup:

var states = new List<States>();
states.Add(new State(){CountryName="United States",
                       StateName="NJ",
                       Capitol="Trenton"});

WorldDatabase.Countries.Add(new Country{Age=237,
                                 Name="United States",
                                 States=states});

WorldDatabase.Countries[0].Name="US";

//assert obviously fails because the names are not linked
Assert.IsEqual(WorldDatabase.Countries[0].Name == states[0].CountryName);

So the problem I'm trying to solve: what's the best way to link the two properties? The way I came up with is injecting an instance of the parent (Country) into the State class. But I'm concerned about the child making changes to the parent when he shouldn't be. Also, it seems like there might be a way to do this with less code that I'm not aware of. Here's the two ways I came up with:

//does a one-time 'binding'
class State
{
    public void BindFrom(Country country)
    {
         CountryName=country.Name;
    }
    public string CountryName{get;set;}
    public string Capitol{get;set;}
    public List<string> Cities{get;set;}
}

//tracks the parent forever
class State
{
    private readonly Country _parent;
    public State(Country parent)
    {
         _parent = parent;
    }

    public string CountryName
    {
     get
       {
      return _parent.Name;
       }
     }
    public string Capitol{get;set;}
    public List<string> Cities{get;set;}
}

What's the name of this pattern (if it is one)? I'd like to read more about it.. and is there an alternative?

Your probelem can be solved by using an interface or base class implemented/inherited by your parent classes.

The interface/base class should only expose the parameters that you wish the child to be able to see or modify. Only the full implementation should allow writing.

class CountryBase
{
    public string Name { get; protected set; }
}

class Country: CountryBase
{
    public string Name { get { return base.Name;} set { base.Name = value;}
}

Country now has full control of the setter for CountryBase.Name . Pass only CountryBase to your State instances.

Alternatively, as an interface (my recommendation);

class ICountry
{
    string Name { get;}
}

class Country: ICountry
{
    public string Name { get; set; }
}

The only pattern I see that is close to your example is the Delegation Pattern (the retrieving of the name is delegated to the state's parent).

Your example reminds me of the difference between agregation and composition : a country has several states and it has no meaning to have a state which doesn't belong to a country.

I guess both solutions have their pros and cons. However I'd change the first one to only know the CountryName instead of passing the country in the constructor. What's the point of breaking data encapsulation with a public setter for CountryName if the country name is altered in a constructor?

It looks like State should have a reference to the Country it belongs to. This kind of pattern is called Object composition .

I don't know what pattern this is, but looking at your example I think the problem your facing is the possibility that a property content is changed and not all different objects that should share the same property contents change with it. If I'm mistaken let me know and I will remove my answer, because the following is based on this assumption.

Using a base class or an interface solve the problem. It just makes sure both have a property that have the same name. If it is at all possible. I would try to implement a different pattern.

One way of doing this is, you could create an object that has a reference in both the Country object and the State object. Than create an object call it Controller that controls the contents of the Name object. If the name has to change the controller is called and te contents is changed accordingly. Make sure the set the property on both the Country object and the State object to Readonly .

A more simple but just as effective way is to create a 'Controller' that is the only object that can change the properties. There does not need to be a reference between the two properties. If the controller is the only object that can change the properties make a method on the controller to change the name of United States to US . If the contoller knows about all collections of countries and states It can then change the properties by doing a lookup on the original content and change that to the new content. If the there are two controllers one for countries and one for states it can call a method on another controller to make the same change. (The existence of the kind of change method can be forced with an Interface or base class)

Think of it this way. A chair has a collection of legs, but the chair itself doesn't know this. It is just a chair. It is the user or owner of the chair the knows it has 4 legs. It could just as well have three legs. If a leg is broken you tell the carpenter to repair the leg of the chair. If you want to add an extra leg to a three legged chair, you tell the carpenter not the chair. In your case if the name of a country is changed you tell it to the country controller. Then the country name gets changed. The country itself just has a name.

The problem with this solution is that in a project that already has a large code base and many people have worked on this is just introducing yet another way of doing things. Old code might not follow your new way of doing things which makes it stand out odd to the rest of the code. Furthermore most of the time refactoring the whole code base isn't feasible nor advisable. Of course slowly introducing this new way might be an option.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM