Ah well, we can't all invent something new.
Anyway, the refactoring in question is "Replace Array With Object", though I've been seeing "Replace List With Object". I first encountered this in C#, and recently again in C++. The method for doing this refactoring I feel is more elegant than in Java or C (or Go for that matter), though primarily because Lists in C# and C++ have array-like syntax available, whereas in Java they do not.
Consider a naive data-structure which represents the "nuclear family":
enum Parents {
DAD_INDEX = 0,
MOM_INDEX = 1
};
list<Person> family = GetFamilyMembersParentsFirst();
Person dad = family[DAD_INDEX];
Person mom = family[MOM_INDEX];
int numChildren = family.size() - 2;
Then the client does this:
How will we handle families with more or less than one parent, or same-sex parents?
Replace List With Object to the rescue!! We need a class which represents the family as a unit. So, first off let's subclass list
class Family: public list<Person>
{
}
Now, change GetFamilyMembersParentsFirst() to return a Family instead of a list<Person>:
Family GetFamilyMembersParentsFirst() {...}
Now, we can change the client to use Family:
enum Parents {
DAD_INDEX = 0,
MOM_INDEX = 1
};
Family family = GetFamilyMembersParentsFirst();
Person dad = family[DAD_INDEX];
Person mom = family[MOM_INDEX];
int numChildren = familiy.size() - 2;
Finally, we can start adding behaviors, like GetNumChildren(). This can be done by extracting the relevant behavior from the client code and moving it to the Family class:
class Family: public list<Person>
{
public:
int GetNumChildren()
{
return size() - 2;
}
}
...
Family family = GetFamilyMembersParentsFirst();
...
int numChildren = family.GetNumChildren();
Now Family can answer our questions about a given family unit. Hooray!
Now, let's take care of those pesky 'mom' and 'dad' variables. Since we're not changing how the class works yet, we'll just extract 'family[DAD_INDEX]' and 'family[MOM_INDEX]' to functions, and move them to the Family class:
class Family: public list<Person>{
public:
Person Dad()
{
return (*this)[DAD_INDEX];
}
Person Mom()
{
return (*this)[MOM_INDEX];
}
}
...
Family family = GetFamilyMembersParentsFirst();
Person dad = family.Dad();
Person mom = family.Mom();
int numChildren = family.GetNumChildren();
Hey, now we can add setters:
class Family: public list<erson>
{
private:
Person dad;
Person mom;
list<Person> children;
public:
void Dad(Person dad) { this->dad = dad; }
void Mom(Person mom) { this->mom = mom; }
void AddChild(Person child) { children.push_back(child); }
...
}
And use them within GetFamilyMembersParentsFirst():
Family GetFamilyMembersParentsFirst()
{
Family family;
...
family.Dad(GetMaleTaxPayer());
family.Mom(GetFemaleTaxPayer());
...
}
Finally, we can change Family to not subclass list<Person>:
class Family
{
...
}
And everything still compiles and works! (Exactly what we would expect out of a good refactoring.)