views:

197

answers:

7

Hi,

I have an existing heirarchy of classes, say something like this:

Business
  - Division
    - ProjectTeam
      - Employee

These classes are instantiated via deserialization.

However, now I need to expose extra fields in the Employee for a particular user of the library i.e. say something like this:

SpecialBusiness (extends Business)
  - Division
    - ProjectTeam
      - SpecialEmployee (extends Employee)
        - Degree

The problem is, I can't just make a class that extends 'Business' because the addition I want to make is to the 'Employee' class.

As I see it I have two options:

  1. Duplicate the heirarchy with 'Special' classes. This means each 'Special' class will have a collection of the original classes and a collection of the new 'Special' classes.

    SpecialBusiness
      - Division AND SpecialDivision (extends Division)
        - ProjectTeam AND SpecialProjectTeam (extends ProjectTeam)
          - Employee AND SpecialEmployee (extends Employee)
            - Degree
    
  2. Somehow retype 'Employee' to 'SpecialEmployee' at runtime for deserialization purposes. Know that I can cast all 'Employee' objects from a SpecialBusiness to 'SpecialEmployee' in the codebase (possibly using helper methods to make it obvious).

Any ideas on how to deal with this problem?

+1  A: 

You cannot change the type of a field at runtime.

That said, why can't you just extend employee if that's what you want to do? Polymorphism states you can plug an object of a subtype into a field of it's supertype? Why do you need to rewrite the parent object?

C. Ross
Because if I extend Employee to SpecialEmployee then I need my ProjectTeam to hold SpecialEmployees and my Division to hold ProjectTeams that hold SpecialEmployees etc. Sure if there was no deserialization I could just add a SpecialEmployee to the ProjectTeam but as it stands deserialization will only fill the fields of 'Employee' not 'SpecialEmployee'
Graphain
Oh, I see. Sorry, this is a tough nut to crack. Serialization tends to break the OO paradigms...
C. Ross
+1  A: 

Well, I'm not completely sure if it is suitable in your situation, but have a look at my answer for this question. You can derive your Division and Employee classes from Decorable and derive SpecialDivision and SpecialEmployee from Decorator<Division> and Decorator<Employee>.

Dmitry
Thanks, it's a possibility but messy in this case I think.
Graphain
A: 

I don't know if it's possible for you to do now, but the whole object structure should use interfaces instead of concrete classes.

Jon Seigel
How would that help?
Graphain
If you have an `IEmployee` interface, then you'd have `Employee` and `SpecialEmployee` implement the interface (which would have all the common fields and methods), with the latter also implementing the extra functionality you need. It would then be up to the consumer of the API to determine the concrete type if they need to use the special functionality.
Jon Seigel
Fair enough. The problem would be deserializer would only deserialize the common fields of IEmployee, not the additional stuff of either Employee or SpecialEmployee
Graphain
The deserializer would always deserialize a concrete class, but return a reference of the interface type.
Jon Seigel
+1  A: 

Most serialization frameworks, e.g. XmlSerializer and BinaryFormatter, provide ways to deserialize streams in a custom way, so you can have your updated/new hierarchy and deserialize old streams into it. What kind of serialization framework are you using?

Anton Tykhyy
I was trying to avoid duplicating the hierarchy that was all. I'm using a custom deserializer.
Graphain
You might get away with updating the hierarchy, but you'll have to dig around your deserializer — since it's custom, I can't tell you anything concrete. With BinaryFormatter, you might use deserialization surrogates; with XmlSerializer, provide special properties and/or use XmlAttributeOverrides.
Anton Tykhyy
Yeah it's pretty much XmlSerializer with some added attributes and ability to get around some limitations like private fields and dictionaries etc.
Graphain
"Private fields" isn't a limitation, it was a conscious design decision... So your problem is creating SpecialEmployee where the xml stream specifies just Employee? With XmlSerializer you can use [XmlType (TypeName = ...)] or an equivalent attribute override.
Anton Tykhyy
+1  A: 

Why can't you muck with your source for deserialization to instantiate the classes that you want at runtime?

Trent
I guess I could override the 'Employees' field in my SpecialProjectTeam and add an attribute that says to make 'SpecialEmployees' good idea!
Graphain
But then I still have to have an entire duplicate hierarchy (oh well).
Graphain
Yeah, the duplicate hierarchy sucks, but I wonder if generics might help you out here. Maybe something like 'ProjectTeam<E> where E is Employee' could help you out. Then figure out how to deserialize into generics. Sounds like a different post, but probably doable.
Trent
A: 

One possible solution (assumes chosen deserialisation method allows private field serialization):

Use the method 1 from the question (i.e. a separate hierarchy) and store the extended versions of the classes in a private field. After deserialization update the regular public non-extended fields with the extended versions and include a method that returns a cast version of these fields.

SpecialProjectTeam
  - (private) SpecialEmployees
  - (public) Employees
  - (public) GetSpecialEmployees (returns Employees field cast as SpecialEmployees)
Graphain
Downside of this method is it's going to deserialize twice as much as it needs to
Graphain
+1  A: 

When the XML states the actual objects types as:

  <Employees>
    <Employee>
      <Name>Bob</Name>
    </Employee>
    <Employee xsi:type="SpecialEmployee">
      <Name>Carol</Name>
      <Degree>Chief</Degree>
    </Employee>
  </Employees>

at least when deferring from declared type (here Employee[] Employees) the XMLSerializer well supports polymorphism at deserialization, as it will create a SpecialEmployee with Name="Carol" and Degree="Chief"; given SpecialEmployee is derived from Employee and the XMLSerializer is told that.
This can either be done by XmlInclude attribute or by providing the specialised types when creating the XMLSerializer.

guest