Layered design in software engineering

Object-orientation in Go

 in object-oriented programming, the key idea is to split code into several small, manageable parts or objects.

Each object has its own identity, data (or attributes), and logic (or behavior). 

A class is a blueprint or a template for objects that share the same behavior and properties.


Encapsulation is the key guiding principle for class design. It implies exposing a contract

for the behavior of objects and hiding volatile implementation details.


The private attributes and methods are hidden inside a capsule according to a need-to-know basis.


Instead of having a long procedural program, the design paradigm is to decompose behavior into small manageable (and ideally reusable) components (objects), each with a well-defined contract (interface).


Doing this effectively allows us, as the class developer, to change implementation while not affecting clients.


A software contract is a formalized documentation of an interaction with a software component. It can be an interface (in the object-oriented sense), an API, or a protocol (for example, TCP).


Contracts allow diverse unconnected components of a system to work together. 


Inheritance is a mechanism in which one object acquires all the properties and behaviors of a parent object.


Inheritance has its pitfalls. It often leads to a hierarchy of classes, and sometimes the behavior of the final object is spread across the hierarchy. 


In an inheritance hierarchy, super classes can often be fragile, because one little change to a superclass can ripple out and affect many other places in the application's code. 


One of the better things that can happen is a compile time error (compiled languages), but the really tricky situations are those where there are no compile time errors, but, subtle behavior changes leading to errors/bugs in fringe scenarios.


An alternative to inheritance is to delegate behavior, also called composition. Instead of an

(is a) this is a (has a) relationship. It refers to combining simple types to make more complex

ones.


Go, have the Composition Over Inheritance principle.


Struct


In Go, instead of Java and C++ classes, the equivalent container for encapsulation is called a struct.


A struct looks like this:

type Animal struct {

  Name string

   canFly bool

}


This defines a new type with the collection of fields mentioned. Once you have defined struct,


you can instantiate it as follows:

   anAnimal := Animal{Name: "Lion", canFly: false}

   fmt.Println(anAnimal.Name)


You can also use dots with pointers to objects (rather than the actual object).


The pointersare automatically dereferenced. 

aLionPtr := &anAnimal

fmt.Println(aLionPtr.age)


Methods are functions that operate on particular struct. They have a receiver clause that

mandates what type they operate on. 


For example, consider the following struct and method:

 type Person struct {

   name string

   age int

 }

 func (p Person) canVote() bool {

    return p.Age > 18

 }

In the previous example, the language construct between the func keyword and the method name is the receiver: ( p Person )


You can view receiver parameters analogous to this or self-identifiers in other languages. There can be only one receiver.


you can define methods using pointer receivers:

func (t * type) doSomething(param1 int) 


And you can use non-pointer method receivers:

func (t type) doSomething(param1 int)


A pointer receiver method makes for Pass-By-Reference semantics, while a non-pointer

one is a Pass-By-Value


Generally, pointer receiver methods are used if either of the following apply:

  1. You want to actually modify the receiver.

  2. The struct is very large and a deep copy is expensive.



Slices and maps act as references, so even passing them as value will allow mutation of the

objects.


Visibility


Managing visibility is key to good class design. Unlike other object-oriented languages, there are no public or private keywords in Go. 

A struct field with a lowercase starting letter is private, while if it is in uppercase, then it's public.


For example, consider a Pigeon package:

  package pigeon

  type Pigeon struct {

    Name string

    featherLength int

  }


Here, inside struct we have the following: Name is a public attribute, and the code outside of the package can reference it.


featherLength is a private attribute, and the code outside of the package cannot reference it


The capitalization-of-initial-letter convention also extends for methods too.

Public methodshave the first letter capitalized, whereas private ones start with a lowercase character


Interface


The interface construct is key to polymorphism in Go.

The interface defines a contract that can be assumed by clients, without knowledge of the actual class that implements the interface.


Interfaces are types that declare sets of methods. Similar to interfaces in other languages, they have no implementation.


Interfaces are at the core of Go's object-oriented support.


Many object-oriented languages define explicit implementations of an interface( extend or semicolon ) however, Go is different, the implementation is implicit  


example:

type Shape interface {

  area() float64

}

type Square struct {

  x1, y1, x2, y2 float64

}

//here we implement the interface

func (s *Square) area() float64 {

  l := distance(r.x1, r.y1, r.x2, r.y2) // calculate distance between 2 points

  return l * l

}


Embedding-composition


Embedding is a mechanism to allow the ability to borrow pieces from different classes. It is the equivalent of multiple inheritance.


type Bird struct {

   featherLength int

   classification string

}

type Pigeon struct {

   Bird

  Name string

}

func main() {

  p := Pigeon{Name :"Tweety", }

  p.featherLength = 10

  fmt.Println(p)

}




تعليقات