I'm used to work with functional programming (mainly Haskell) and I'm beginning with OO (scala).
I'm having troubles on translating my code. For instance, that's my Haskell definition of a B-tree:
data BTree a =
Leaf
|Node2 (BTree a) a (BTree a)
|Node3 (BTree a) a (BTree a) a (BTree a)
deriving (Eq,Read,Show)
It's quite simple. My tree is empty, or it has a value and is a father of two trees or it is a father of 3 sub trees.
What is it on OO? I have no clue. I just can't figure out how can I do it in a sane way.
Even though OOP promises to address modularity and improve reusability, it fails to deliver on its promises (more on this later). OOP code encourages the use of shared mutable state, which has been proven to be unsafe time and time again. OOP typically requires a lot of boilerplate code (low signal-to-noise ratio).
Functional programming mainly supports abstraction over data and abstraction over behavior. Object-oriented programming mainly supports abstraction over data only. Functional programming provides high performance in processing large data for applications. Object-oriented programming is not good for big data processing.
First, OO and FP are at least compatible in one sense: the same person can write both types of software, and you don't need to “pick a side.” What's more, the same programming language can offer both OO and functional features, and the same project can use OO features in some parts and functional features in others.
Some good answers here, but I think they all miss the opportunity to show the point you are missing. So, you have shown this:
data BTree a =
Leaf
|Node2 (BTree a) a (BTree a)
|Node3 (BTree a) a (BTree a) a (BTree a)
deriving (Eq,Read,Show)
And asked how do you implement this in an object-oriented fashion. So, here it is:
Most important thing
trait Tree[A] {
// not required because it is inherited from AnyRef
// def equals(other: Any): Boolean
// not required because it is inherited from AnyRef
// def toString: String
// does not belong in the object
// def fromString(input: String): Tree[A]
// Stuff that is missing but is needed
def isEmpty: Boolean
def value: Option[A]
def subtrees: Seq[Tree[A]]
def iterator: Iterator[A]
def depthFirstIterator: Iterator[A]
def breadthFirstIterator: Iterator[A]
}
So, here's the deal: when you speak of object orientation, that you have a BTree, a finger tree, or whatever other tree structure is irrelevant. In fact, it should be hidden. What is relevant is what you can do with it.
You are having trouble doing this because you are approaching the problem from precisely the direction you shouldn't.
The not-so-important thing
sealed abstract class BTree[A] extends Tree[A]
object BTree {
def apply[A](input: String): BTree[A] = { /* factory */ null.asInstanceOf[BTree[A]] }
private case object Leaf extends BTree[Nothing] {
// method implementation
}
private case class Node2[A](value: A, private one: BTree[A], private two: BTree[A]) extends BTree[A] {
// method implementation
}
private case class Node3[A](value: A, private one: BTree[A], private two: BTree[A], private three: BTree[A]) extends BTree[A] {
// method implementation
}
}
Now here you actually offer an implementation, but the details of the BTree are completely hidden. You only get to use the methods that Tree
has defined.
This is the ideal object oriented architecture: clients depend on interfaces, data structure is hidden.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With