0

Let's say library A, B and C are all custom math libraries. They may or may not use the same data types. The libraries have methods that accept data type inputs like ProcessAsync(DataTypeA) or ClusterByFeature(IDataTypeB, Func<IDataTypeB, double>).

These libraries will be developed for modularity. E.g. Future graduate students can come and use the library for their needs.

I thought that these libraries should:

  1. either provide concrete implementations of the inputs they accept and manipulate on, like DataTypeA, DataTypeB, DataTypeC
  2. or, request that the users supply an object of type IDataTypeA, IDataTypeB, IDataTypeC

Which design I should choose seems to be dependent on many factors.

I've come up with a few:

  • If it's just a container, it should be an interface
  • If DataTypeA, DataTypeB, DataTypeC are all very similar, use interfaces - let the invoking framework supply the implementation by inheriting all the interfaces
  • If DataTypeA requires complicated operations, use classes
  • If DataTypeA requires simple operations, use interfaces and a DataTypeAManipulator class

I see other open-source libraries overwhelmingly use classes instead of interfaces, so I think classes should be the way to go. But is there no place for data interfaces then?

--

A better example of the problem that I faced: There's a 3D object that needs to be manipulated on. Library A handles vertices, Library B handles vertices and deformation by bone joints, Library C handles vertices and texture mapping. 3 different data objects - they're actually different views of a single data object, but the libraries don't/shouldn't know that! So should I use data interfaces to show different perspectives of a 3D object, or copy values to data classes instead.

5
  • 1
    Have you considered making these methods generic and adding the interfaces as constraint? Oct 15, 2015 at 7:24
  • @CodesInChaos I don't understand the point of doing so.. Regardless of the library declaring data classes or data interfaces, what scenarios would generic constraints help in?
    – Cardin
    Oct 15, 2015 at 9:10
  • 1
    1) You can have more than one generic constraint, wheras C# doesn't support intersection types. 2) With ClusterByFeature(IDataTypeB, Func<IDataTypeB, double>) the user defined function gets passed the interface. It benefit from receiving the concrete type instead. But you didn't specify enough context to really analyze your problem. Oct 15, 2015 at 9:13
  • @CodesInChaos Ah I see its usefulness now. Let me give a better context then. There's a 3D object that needs to be manipulated on. Library A handles vertices, Library B handles vertices and deformation by bone joints, Library C handles vertices and texture mapping. 3 different data objects - they're actually different views of a single data object, but the libraries don't/shouldn't know that! So should I use data interfaces to show different perspectives of a 3D object, or copy values to data classes instead.
    – Cardin
    Oct 15, 2015 at 9:21
  • It'd either use interfaces or projection functions. Or possibly the interface as the default projection function. But that doesn't really work will if the object is supposed to be mutable. Oct 15, 2015 at 9:27

1 Answer 1

2

Data classes make sense when they are used as a container for data (a POCO in .NET or POJO in Java). They contain no behaviour and simply serve to encapsulate data and simplify code, eg:

int CalculateSurface(int x1, int y1, int x2, int y2)

vs

int CalculateSurface(Rect area)

When you pass a data class, you could actually write the method so as to take the arguments separately. You obviously don't WANT to do that because it seriously clutters up your code, but you could.

A data interface makes sense when you actually need logic or behaviour, which means methods on the passed object need to be implemented. It also makes sense when you want the object you are working on to perform some operation and you need to pass it a parameter, in which case you need a contract for this method, hence an interface.

5
  • So by "when you actually need logic or behaviour", you're referring to behaviour that the user has to define, like IEnumerable.MoveNext() etc.? In other cases where the library knows how to perform this logic, I assume it's fine to still use a data class that has instance methods.
    – Cardin
    Oct 15, 2015 at 9:00
  • And by extension, I suppose if it's just a data container, tuples/structs would work fine too as a data class..
    – Cardin
    Oct 15, 2015 at 9:00
  • 2
    Good answer. Just one comment. In my experience combining data and logic in the same class tends to get you into a world of trouble. This is because logic you may want to reuse can easily get tightly coupled with the data class it was first invented for. Having logic in separate classes reduces this risk significantly and makes for easier to understand code. Oct 15, 2015 at 9:19
  • @JonathanvandeVeen, it simply isn't possible to upvote your comment enough. "combining data and logic in the same class tends to get you into a world of trouble". Every OO developer ever needs to have this drummed into them until they bleed.
    – David Arno
    Oct 15, 2015 at 9:37
  • @JonathanvandeVeen Assuming the rules above still apply, then to avoid users from repurposing the data class beyond its original use, it can be marked as final/uninheritable. Or if you meant data interfaces with data and logic, then maybe 1 data object interface and 1 data manipulator interface.
    – Cardin
    Oct 15, 2015 at 9:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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