The Diametric Opposition of Objects and Data Structures

The Diametric Opposition of Objects and Data StructuresTravis PetersenBlockedUnblockFollowFollowingMay 17Photo by Julien Moreau on UnsplashData/Object Anti-Symmetry is basically a fancy term for the fact that data structures and objects are pretty much direct opposites.

Objects are classes that obscure their data and expose their functionality.

Data structures are classes that expose their data and have little to no functionality.

Let’s flesh out that comparison a little more, and discuss the benefits and drawbacks of each construct.

ObjectsObjects make it easy to add new data types without altering existing functionality (typically through polymorphism) because their data is obscured to the user — creating a new object that implements the same interface as others with shared functionality affords the user ignorance of what type of object they’re operating on.

Let’s look at an example:public class Line implements Polynomial { private double slope; private double yIntercept; public double differentiate(double x) { return slope*x; }}public class Parabola implements Polynomial { private double quadraticCoefficient; private double linearCoefficient; private double constantTerm; public double differentiate(double x) { return 2*x*quadraticCoefficient + linearCoefficient; }}These classes obscure their data in private instance variables.

This way it’s easy to add new Polynomial data types without having to alter the functionality of Line or Parabola, or know anything about their internal data to implement that functionality.

If I want to create a Cubic type, then every other implementation of Polynomial will remain untouched, and I can still just call differentiate() on anything that implements Polynomial without having to know it’s data type:public class Cubic implements Polynomial { private double cubicCoefficient; private double quadraticCoefficient; private double linearCoefficient; private double constantTerm; public double differentiate(double x) { return 3*x*x*cubicCoefficient + 2*x*quadraticCoefficient + linearCoefficient; }}The big drawback here is that adding new functionality requires altering every data type.

For instance, say you want to add integrate behavior to the Polynomial interface.

This would require individually adding an implementation of it to Line, Parabola, Cubic, etc.

to avoid violating the Interface Segregation Principle.

Or, depending on the language you’re using, your code simply won’t compile.

Data StructuresBy exposing data without offering the functionality to manipulate it, data structures make it easy to add functionality to a program without needing to be altered themselves.

In a procedural approach, the functionality associated with a given data type can be implemented in a separate class, so adding functionality simply requires adding a new method to that class:public class Line { public double slope; public double yIntercept;}public class Parabola { public double quadraticCoefficient; public double linearCoefficient; public double constantTerm;}public class Calculus { public double differentiate(Line line, double x) { return line.

slope*x; } public double differentiate(Parabola para, double x) { return 2*para.

quadraticCoefficient*x + para.

linearCoefficient; }}These data structures expose their data in public instance variables so it can be operated on in the Calculus class.

In this pattern, one could easily add functionality without having to alter any of the existing data structures.

Now, if I want to implement an integrate method in Calculus, I don’t have to change anything about Line or Parabola:public class Line { public double slope; public double yIntercept;}public class Parabola { public double quadraticCoefficient; public double linearCoefficient; public double constantTerm;}public class Calculus { public double differentiate(Line line, double x) { return line.

slope*x; } public double differentiate(Parabola para, double x) { return 2*para.

quadraticCoefficient*x + para.

linearCoefficient; } public double integrate(Line line, double x) { return (x*x*line.

slope)/2 + x*line.

yIntercept; } public double integrate(Parabola para, double x) { return (x*x*x*para.

quadraticCoefficient)/3 + (x*x*para.

linearCoefficient)/2 + x*constantTerm; }}The upshot of this pattern is that adding a new data type means changing every existing piece of functionality.

If I want to add a Cubic class, I have to create an implementation of every method in Calculus for it.

Hopefully the differences, benefits, and drawbacks of these constructs are now clear, as well as why this anti-symmetry is worthy of consideration when planning one’s design.

It can feel like a real Sophie’s Choice kind of situation, but to me it comes down to where you want to be able to be flexible.

In a nutshell, objects prioritize flexibility in data types that comes back to bite you if you want to add functionality, while data structures prioritize flexibility in functionality that comes back to bite you if you want to add a data type.

See?.Opposites.

.

. More details

Leave a Reply