Today's mission involves leveraging various programming principles in Go to tackle complex tasks effectively. When principles like Encapsulation, Abstraction, Polymorphism, and Composition are blended, the resulting code becomes streamlined and easier to manage.
Our goal is to explore two real-world examples, demonstrating how these principles can seamlessly orchestrate solutions using structs
, interfaces
, and composition
in Go.
We'll design an online library system to reinforce our understanding of Encapsulation
and Polymorphism
in Go. Encapsulation
will help us guard the attributes of books, members, and transactions, ensuring controlled access. Polymorphism
will illustrate its utility by enabling a single interface to represent different underlying forms, such as digital and print versions of books.
Go1package main 2 3import ( 4 "fmt" 5) 6 7// Struct defining a library member 8type Member struct { 9 name string 10} 11 12// Method for a member to check out a book 13func (m *Member) CheckOutBook(book Book) { 14 fmt.Printf("%s checked out %s book %s.\n", m.name, book.GetBookType(), book.GetTitle()) 15} 16 17// Interface for different types of books 18type Book interface { 19 GetBookType() string 20 GetTitle() string 21} 22 23// Struct representing a digital book 24type DigitalBook struct { 25 title string 26} 27 28// Implementing Book interface for DigitalBook 29func (d DigitalBook) GetBookType() string { 30 return "Digital" 31} 32 33func (d DigitalBook) GetTitle() string { 34 return d.title 35} 36 37// Struct representing a physical book 38type PhysicalBook struct { 39 title string 40} 41 42// Implementing Book interface for PhysicalBook 43func (p PhysicalBook) GetBookType() string { 44 return "Physical" 45} 46 47func (p PhysicalBook) GetTitle() string { 48 return p.title 49} 50 51// Struct representing a library to manage members and books 52type Library struct { 53 members []Member 54 books []Book 55} 56 57// Method to add a member 58func (l *Library) AddMember(member Member) { 59 l.members = append(l.members, member) 60} 61 62// Method to add a book 63func (l *Library) AddBook(book Book) { 64 l.books = append(l.books, book) 65} 66 67func main() { 68 myLibrary := Library{} 69 70 alice := Member{"Alice"} 71 bob := Member{"Bob"} 72 73 myLibrary.AddMember(alice) 74 myLibrary.AddMember(bob) 75 76 digitalBook := DigitalBook{"The Go Handbook"} 77 physicalBook := PhysicalBook{"Learning Go Design Patterns"} 78 79 myLibrary.AddBook(digitalBook) 80 myLibrary.AddBook(physicalBook) 81 82 alice.CheckOutBook(digitalBook) // Prints: Alice checked out Digital book The Go Handbook. 83 bob.CheckOutBook(physicalBook) // Prints: Bob checked out Physical book Learning Go Design Patterns. 84}
In this code snippet, Encapsulation
is demonstrated by using Go structs to contain member and book details. Polymorphism
is illustrated by how both DigitalBook
and PhysicalBook
structs implement the Book
interface, allowing them to be used interchangeably when identifying the type of a book. This setup shows how polymorphism in Go allows working with different types through common interface definitions.
Encapsulation
secures member and book information within their respective structs.Polymorphism
permits uniform handling of different book types, enhancing system adaptability.
Now, we'll develop a shape-drawing application capable of rendering various shapes using Abstraction
and Composition
.
Abstraction
reduces complexity in handling different shapes.Composition
facilitates the creation of composite shapes.
Here's how these principles translate into our shape-drawing application in Go:
Go1package main 2 3import "fmt" 4 5// Interface defining a Shape 6type Shape interface { 7 Draw() 8} 9 10// Struct representing a Circle 11type Circle struct{} 12 13// Implementing Draw method for Circle 14func (c Circle) Draw() { 15 fmt.Println("Drawing a circle.") 16} 17 18// Struct representing a Square 19type Square struct{} 20 21// Implementing Draw method for Square 22func (s Square) Draw() { 23 fmt.Println("Drawing a square.") 24} 25 26// Struct for composing multiple shapes 27type ShapeComposite struct { 28 shapes []Shape 29} 30 31// Method to add a shape to the composite 32func (sc *ShapeComposite) AddShape(shape Shape) { 33 sc.shapes = append(sc.shapes, shape) 34} 35 36// Implementing Draw method for composite shapes 37func (sc ShapeComposite) Draw() { 38 for _, shape := range sc.shapes { 39 shape.Draw() 40 } 41} 42 43func main() { 44 circle := Circle{} 45 square := Square{} 46 47 // Drawing individual shapes 48 circle.Draw() // Output: Drawing a circle. 49 square.Draw() // Output: Drawing a square. 50 51 // Creating a ShapeComposite instance for composite shapes 52 compositeShape := ShapeComposite{} 53 54 // Adding individual shapes to the composite 55 compositeShape.AddShape(circle) 56 compositeShape.AddShape(square) 57 58 // Drawing the composite shape 59 compositeShape.Draw() 60 // Output: 61 // Drawing a circle. 62 // Drawing a square. 63}
-
Abstraction: Here, the
Shape
interface abstracts the specific details of drawing shapes, ensuring that all shapes conform to having aDraw()
method. This interface acts as a blueprint for all shapes. -
Composition: The
ShapeComposite
struct exemplifies composition by combining multiple shapes. It can hold and render multiple shapes as a single unit. Composition is effectively used to manage a group of shapes together.
Well done! You combined multiple programming principles using Go's unique features of structs
and interfaces
to tackle complex tasks. By examining real-world examples, we learned how these principles apply in Go. Now, it's time to practice these skills. Active coding will solidify concepts, turning knowledge into expertise. Let's start coding!