Data structures VS Objects — Swift

Senn
Nerd For Tech
4 min readNov 9, 2021

--

I was reading Chapter 6 of Clean Code by Robert C. Martin, and I was pretty confused by the difference between an object and a data structure.
I had to read it multiple times to understand what makes an object an object and what makes a data structure a data structure and how they differ from each other. It took me more than one, two, three readings to better understand it but it’s safe to say I understood the gist of it. I then decided to apply it to my favorite language (Swift) and share my understanding and a few examples.

Data structures :

Data structures should expose their data and does not need to have any meaningful behaviors. They should serve as containers, a way to access data so that external parties could use them to create meaningful behaviors.

An example of this in Swift could be an Enum. Enums are perfect for defining data

enum Word {  case circle  case square  case rectangle}

In this data structure, notice how only the data is being exposed and there are no real behaviors or functions that depends upon using it. The benefits of this, you could have many different functions using this data structure.

Objects :

Objects hide their data by using abstraction but would expose behaviors and functions. This can be done by making their fields private while exposing the functions that will manipulate their private data.
Also, an object varies from a data structure in the sense that its methods or behaviors should not be a way to access its data but rather to perform an action that will manipulate those data. If an object has a function that just returns the value of one of its private data, it is not an object but still a data structure
What do I mean by that ? Take a look at this code:

class Square {  private var sideLength: Int  init(sideLength: Int) {    self.sideLength = sideLength  }  func getSideLength() -> Int {     return sideLength    }}

This would still be considered a data structure since this class exposes its data through``getSideLength()`` .
To make it an object, we need to create a new behavior that will not expose its data but rather manipulate it thus conserving the concept of abstraction.

Just like this:

class Square {private var sideLength: Intinit(sideLength: Int) { self.sideLength = sideLength}func findArea() -> Int {  return sideLength * sideLength }}

Notice how findArea() just manipulates the data and hides implementation.

What are the advantages and inconvenience of using Objects VS data structure ?

The book illustrates those differences by highlighting the pros and cons of using Procedural code with data structures and using object oriented code.

Procedural code using data structures

enum Word { case circle case square case rectangle}class Translation { func frenchTranslation(word: Word) -> String {  switch word {   case .circle:    return "rond"   case .square:    return "carré"   case .rectangle:    return "rectangle"  } }}

Notice ```enum Word``` is a data structure and the frenchTranslation() function operates on the three given words. The enum Word has no behaviors but the Translation class uses that enum to create behaviors.
The advantage of this procedural flow using data structure is that I can keep adding more functions to the Translation class without the enum Word being affected. However once I add a new word to the enum let’s say case triangle, all the functions I’ve added to the Translation class need to be updated to take into account that new word.
Therefore, adding new behaviors is easy however adding a new data structure can be tedious since every functions need to accommodate that newly added data structure.
Now let’s take a look at the object oriented way.

Object Oriented way

protocol Shape {  func findArea() -> Int}class Square: Shape { private var side: Int init(side: Int) {  self.side = side} func findArea() -> Int {  return side * side}}class Rectangle: Shape { private var height: Int private var width: Int init(height: Int, width: Int) {  self.height = height  self.width = width } func findArea() -> Int {    return height * width    }}

We have a protocol called Shape that gives us a method to be implemented which is findArea()
We then create objects that implement Shape and expose the implementation of that method.
Notice how our objects have private data that are hidden and are used to manipulate the data and give us the area of the given object.
In the object oriented solution, it is pretty easy to add new objects — all we have to do is for that object to implement the protocol Shape and all is gravy!
However if we were to add a new behavior to the protocol Shape such as : draw3DRepresentation

protocol Shape {func findArea() -> Intfunc draw3DRepresentation() -> UIImage}

We’d then need to update every objects that implement that protocol.
Thus we could say, with the object oriented way, it’s easy to add new objects but tedious to add new behaviors.

Procedural code (code using data structures),

Makes it easy to add new functions without changing the existing data structures.

Makes it hard to add new data structures because all the functions must change.

OO code (code using object oriented),

Makes it hard to add new functions because all the existing classes must change.

Makes it easy to add new classes without changing existing functions.

So, the things that are hard for OO are easy for Procedural, and vice-versa.

Just like the book mentioned, they both have their advantages and inconvenients.
Object oriented is more convenient when we’re more likely to be adding more objects or data types and no new behaviors.

However procedural code is more convenient when we’re more likely to be adding new behaviors rather than new data structures.

Helpful links

https://www.linkedin.com/pulse/clean-code-chapter6-objects-data-structures-mahmoud-ibrahim

https://www.youtube.com/watch?v=-YoX9KRW89E&t=314s

--

--