Object-oriented programming (OOP) is a programming paradigm based on the concept of “objects”, which can contain data and code: data in the form of fields (often known as attributes or properties), and code, in the form of procedures (often known as methods).
A feature of objects is that an object’s own procedures can access and often modify the data fields of itself (objects have a notion of this or self). In OOP, computer programs are designed by making them out of objects that interact with one another. OOP languages are diverse, but the most popular ones are class-based, meaning that objects are instances of classes, which also determine their types.
Encapsulation is an object-oriented programming concept that binds together the data and functions that manipulate the data, and that keeps both safe from outside interference and misuse. Data encapsulation led to the important OOP concept of data hiding.
Subtyping – a form of polymorphism – is when calling code can be agnostic as to which class in the supported hierarchy it is operating on – the parent class or one of its descendants. Meanwhile, the same operation name among objects in an inheritance hierarchy may behave differently.
This is another type of abstraction that simplifies code external to the class hierarchy and enables strong separation of concerns.
Sounds familiar ? interfacing is a more powerful tool for this purpose
I think this is why golang provides powerful interface mechanism and convenient embedded composition and no inheritance.
c++:
class Fish
{
public:
Fish(std::string name) {
this.name = name;
};
void swim();
private:
std::string name;
};
void Fish::swim()
{
std::cout << name << " swims" << std::endl;
}
java:
class Fish {
private String name;
public Fish(String name) {
this.name = name;
}
public void swim() {
System.out.println(name + " swims");
}
}
golang:
type Fish struct {
name string
}
func (f *Fish) Swim() {
fmt.Printf("%s swims\n", f.name)
}
func NewFish(name string) *Fish {
return &Fish{name: name}
}
features:
this
pointerinheritance with java
public class Animal {
public void walk() {
System.out.println("walking")
}
public void jump() {
System.out.println("jumping")
}
}
public class Dog extends Animal{
@override
public void jump() {
System.out.println("dog jumping")
}
public void bark() {
System.out.println("barking")
}
public static void main() {
Dog dog = new Dog();
dog.bark(); // subclass method
dog.walk(); // parent class method
dog.jump(); // subclass method
}
}
composition with Golang
type Animal struct {
}
func (animal *Animal) Walk() {
fmt.Printfln("walking")
}
func (animal *Animal) Jump() {
fmt.Printfln("jumping")
}
func NewAnimal() *Animal {
return &Animal{}
}
type Dog struct {
*Animal // embedded variable, can be referenced as Dog.Animal
}
func (dog *Dog) Bark() {
fmt.Printfln("barking")
}
func (dog *Dog) Jump() {
fmt.Printfln("dog jumping")
}
func NewDog() *Dog {
return &Dog{
&Animal{}
}
}
func main() {
dog = NewDog()
dog.Bark()
dog.Walk() // equals to Dog.Animal.Walk
dog.Jump() // override *Animal.Jump
}
variable embedding and method look-up make delegate-pattern very common in golang programs.
embedded variables can be interfaces, which makes it more flexible
type Dog struct {
Animaler
}
func (dog *Dog) Bark() {
fmt.Printfln("barking")
}
func (dog *Dog) Jump() {
fmt.Printfln("dog jumping")
}
func NewDog(animaler Animaler) *Dog {
return &Dog{
Animaler
}
}
So, embedded anonymous fields make it easy to mock inheritance. But what about polymorphism ?
in java, we can do this:
Animal animal = new Dog();
animal.Jump(); // output: dog jumping
but in golang, this is not going to work
Animal *animal = NewDog() // error, type not match
polymorphism is super simple with interface because interfaces are duck-typed, which means a type implements an interface if it provides definitions for all the methods declared in the interface
type Animaler interface {
func Walk()
func Jump()
}
Animaler animal1 = NewAnimal()
animal1.Jump() // output jumping
Animaler animal2 = NewDog()
animal2.Jump() // output dog jumping
duck-typed interface makes it unnecessary to write adapter
classes in languages like java, which implemented interfaces must be declared.
Inheritance implies is-a relationship, but sometimes, one can be many things or play many roles, for example, an intern is both an employee and a student, so in order to maintain the is-a relationship, we have to use multiple inheritance.
public class Intern extends Employee, Student
but we all know multiple inheritance is bad !
one of the solutions would make Employee and Student abstract classes with no data, which is just like interfaces !
golang has a set of features to encourage you to write the best OO programs