Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I know of Interfaces, Abstract classes that serve as Base class, I've been reading up on loose coupling, ... But I can't come up with a decent solution when a property of a class changes, and the new type needs to be implemented in other classes using this updated class.

Example

Below is a simplified representation of a real life situation. Invoice contains a Contact object which has an Address property.

Abstract Class Contact{
    string Name{get;set;}
    string Address{get;set;}
}
Class Customer:Contact{
    string CustomerNumber{get;set;}
}
Class Invoice{
    string DocumentNumber{get;set;}
    Customer Customer{get;set;}
}

Next, we decide to upgrade the Contact class to contain multiple Addresses. Of which the Invoice will need one.

Class Address{
    string Street{get;set;}
}
Abstract class Contact{
    string Name{get;set;}
    List<Address> Addresses{get;set;}
}

Logically, the Invoice class needs to be changed to something like

Class Invoice{
    string DocumentNumber{get;set;}
    Customer Customer{get;set;}
    Address InvoiceAddress{get;set;}
}

Problem

You immediately see that everywhere I used the string property Address before, I will now have to change it to an occurence of the type Address

As stated before, this is just a simplified example. The real life problem would be much, much bigger as contacts will be used throughout the entire software.

Conclusion

I want to develop the software with a principle that will keep my software light for new versions of certain modules. So that if I decide to create a new Contact module, the impact to the other modules is close to non-existing. So, the added functionality can be implemented gradually.

I want to know how I can tackle such versioning problems. This project is currently still in the architectural design stage, and I want this sorted out before we continue.

share|improve this question

closed as unclear what you're asking by gnat, GlenH7, MichaelT, Michael Kohne, Dynamic Mar 2 at 18:22

Please clarify your specific problem or add additional details to highlight exactly what you need. As it's currently written, it’s hard to tell exactly what you're asking. See the How to Ask page for help clarifying this question.If this question can be reworded to fit the rules in the help center, please edit the question.

1  
"Programmers - Q&A for professional programmers interested in conceptual questions about software development" Tell me how my question fails at that. And Also in the link you provided it states "While long and complete answers are a good thing, questions that would require books to answer completely don't make good questions." Do you think one would need a book to answer my question? Please be constructive. –  Uw Concept Feb 28 at 10:25
1  
But there is an actual question to be solved! Which combination of Interfaces, Abstract classes or properties do I need to use in order to have a minimal impact when a property changes in a class that another class uses. Is my question really that unclear? I mean, can't you see that I took my sweet time to ask my question as complete as possible? I used code samples, devided my question in three chapters. What else do I need to do?! –  Uw Concept Feb 28 at 10:47
1  
@UwConcept: I rephrased your last paragraph to address the concern that @gnat raised. If you don't like it, you could roll it back.` –  Bart van Ingen Schenau Feb 28 at 11:06
    
Code Complete sec 5.3 design heuristics has some great advice that applies here. Use abstractions, encapsulate implementation details, inherit when it simplifies, hide secrets (that which is hidden, can be changed without breaking code in classes that don't see), etc. –  Fuhrmanator Mar 1 at 6:45

3 Answers 3

up vote 1 down vote accepted

When a property of a class changes, you have to modify all callers who use that property. Its not possible to magically have an interface change that doesn't require changes to callers who use that interface.

However, you can isolate internal properties from external interfaces - generally this is as simple as not using getters and setters. They are a true anti=pattern and should be avoided at all times. See, all you are doing by implementing a getter is exposing an internal variable using a method.There's little decoupling or hiding going on. Change the internal variable type.. and you have to change the getter too, which means you have to change all your clients. You might as well make all your members public and cut out the middleman. See this post for more detail. If you provide true methods that get data from your class, then you can hide the internal variables. If invoice number changes from a string to an int, you can keep the old method that exposed the InvoiceNumber and cast it. Obviously this will not work in every case, but it can help hide the implementation from your callers.

The other thing you can do is implement multiple interfaces on your class, so that old callers can still call the old interface while new ones can access Interface2 or InterfaceEx or whatever.

share|improve this answer
    
This helped me understand the matter best, and even helped me understand the other answer better. So I will mark this as the answer. Thanks! –  Uw Concept Feb 28 at 14:46
    
Tl;dr use information hiding more. –  Fuhrmanator Mar 1 at 6:47

I want to develop the software with a principle that will keep my software light for new versions of certain modules. So that if I decide to create a new Contact module, the impact to the other modules is close to non-existing. So, the added functionality can be implemented gradually.

Design your class so that consumers are isolated from the changes, so:

class Contact {
  string Address {get; set;} 
} 

would become

class Contact {
  List<Address> Addresses {get; set;}
  Address getAddress() { return Addresses[0].StreetAddressLine; }
}

As stated before, this is just a simplified example. The real life problem would be much, much bigger as contacts will be used throughout the entire software.

Once you see a logical entity more than once across classes, consider extracting it into a class. The change could have happened once you noticed that address was in both Contact and Invoice, and this would have been fixed before you needed to support multiple addresses.

Extract Class design pattern

I am very curious of the suggestions of other programmers / software architects, and how you tackled versioning problems like these in your projects. This project is currently still in the architectural design stage, and I want this sorted out before we continue.

If you wrote code, and then refactored it, you have moved past the design stage and into implementation. Consider improvement in the following areas:

1) Designing small components that can be quickly implemented

2) Isolating other components from changes to this component

Do not design for theoretical changes, instead have a development cycle that is short enough to react to change. If the design and implementation are fairly short, the process of making both the original code and the changed code would have been shorter than combining the two.

share|improve this answer
    
Your answer does help, but how will I know in the Invoice class which Address of the Contact I am using? I would have to alter the Invoice class to add a property of type Address because the string property Address is no longer valid (because there are now multiple options) –  Uw Concept Feb 28 at 11:14

Follow S.O.L.I.D Design Principles and don't be afraid to refactor as soon as you feel the need to. Remove unused code, it can make refactoring difficult in the future.

Don't try to build a perfect design at once. Make it work then make it better. Follow Pareto principle: 20% efforts gives 80% of the result.

share|improve this answer
    
Thank you for your advice. I'll look deeper in these principles! –  Uw Concept Mar 4 at 13:53

Not the answer you're looking for? Browse other questions tagged or ask your own question.