Go design pattern (1)

Introduction

Recently, I have thought about the business written in the past few years, and they are all realized with process-oriented thinking. Process-oriented

One is because everyone writes like this and rarely thinks about whether there is a better realization

The second is that the business is simple and can be easily implemented using process-oriented programming

Third, the business needs to go online quickly, and the process-oriented approach is faster and more direct

But the disadvantages are also obvious. As the business continues to accumulate, project changes are more difficult. It is necessary to constantly sort out how the predecessors wrote and what the impact is. A lot of tests need to be done to ensure that the functions are normal. Sometimes even similar or iterative requirements cannot be guaranteed to go online quickly. And even if it consumes a lot of manpower, it is difficult to guarantee that there are no omissions. At this time, I think of object-oriented.

Four characteristics

Object-oriented programming has four major characteristics

  1. Encapsulation features
    Encapsulation is also called information hiding or data access protection. By exposing a limited access interface, the class authorizes the outside to access internal information or data only through the method provided by the class. It requires a programming language to provide permission access control syntax to support, such as the private, protected, and public keywords in Java. The significance of the encapsulation feature is that on the one hand, it protects the data from being arbitrarily modified and improves the maintainability of the code; on the other hand, it only exposes a limited number of necessary interfaces and improves the usability of the class.
  2. Abstract Features
    Encapsulation mainly talks about how to hide information and protect data. The abstraction talks about how to hide the specific implementation of a method, so that users only need to care about what functions the method provides, and do not need to know how these functions are implemented. Abstraction can be achieved through interface classes or abstract classes, but it does not require special grammatical mechanisms to support it. The meaning of abstract existence, on the one hand, is to improve the scalability and maintainability of the code, modify the implementation without changing the definition, and reduce the scope of code changes; on the other hand, it is also an effective means to deal with complex systems, which can effectively filter out Information that needs attention.
  3. Inheritance characteristics
    Inheritance is used to express the is-a relationship between classes, divided into two modes: single inheritance and multiple inheritance. Single inheritance means that a subclass only inherits one parent class, and multiple inheritance means that a subclass can inherit multiple parent classes. In order to inherit this feature, the programming language needs to provide a special syntax mechanism to support it. Inheritance is mainly used to solve the problem of code reuse.
  4. Polymorphism characteristics
    Polymorphism means that the subclass can replace the parent class. In the actual code running process, the method of the subclass is called. This feature of polymorphism also requires programming languages ​​to provide special syntax mechanisms to achieve, such as inheritance, interface classes, and duck-typing. Polymorphism can improve the scalability and reusability of the code, and it is the code realization basis for many design patterns, design principles, and programming skills.

achieve

If you want to use object-oriented programming to implement business logic, you must master how to use this language to implement the four major features. On the basis of mastering this knowledge, it is possible to use design patterns flexibly and design a good architecture. When learning new languages ​​in the future, object-oriented implementation needs to be one of the focuses. To see the following content, you need to master basic Go language knowledge.

Encapsulation

The implementation method of encapsulation in Go is to use a structure and add corresponding methods to the structure.

The general implementation steps are as follows:

  1. Lower case the first letter of structure and field;
  2. Provide a factory mode function for the package where the structure is located, with the first letter capitalized, similar to a constructor;
  3. Provide a Set method with the first letter capitalized (similar to public in other languages), which is used to judge and assign values ​​to attributes;
  4. Provides a Get method (similar to public in other languages) with an initial capital letter to get the value of an attribute.

Example: For employees, you cannot check privacy such as age and salary at will, and perform reasonable verification on the entered age. The code structure is as follows

package model

import "fmt"

type person struct {
    
    
   Name string
   age  int //其它包不能直接访问..
   sal  float64
}

//写一个工厂模式的函数,相当于构造函数
func NewPerson(name string) *person {
    
    
   return &person{
    
    
      Name: name,
   }
}

//为了访问age 和 sal 我们编写一对SetXxx的方法和GetXxx的方法
func (p *person) SetAge(age int) {
    
    
   if age > 0 && age < 150 {
    
    
      p.age = age
   } else {
    
    
      fmt.Println("年龄范围不正确..")
      //给程序员给一个默认值
   }
}
func (p *person) GetAge() int {
    
    
   return p.age
}

func (p *person) SetSal(sal float64) {
    
    
   if sal >= 3000 && sal <= 30000 {
    
    
      p.sal = sal
   } else {
    
    
      fmt.Println("薪水范围不正确..")
   }
}

func (p *person) GetSal() float64 {
    
    
   return p.sal
}

Regarding this code, there are two points that need to be explained

  1. About capitalization
    • Functions starting with a lowercase letter are only visible in this package, and functions starting with a capital letter can be used by other packages. This rule also applies to the visibility of types and variables.
    • Capitalization affects visibility. The beginning of a capital letter is equivalent to public, and the beginning of a lowercase letter is equivalent to private. This approach not only eliminates the public and private keywords, but also unifies the naming style.
  2. Structure method parameters
    • If it is required that the object must be passed by a pointer, this is sometimes an additional cost, because the object is sometimes small (for example, 4 bytes), and it is not cost-effective to pass by a pointer.
    • Only when you need to modify the object, you must use pointers.

inherit

Go language does not support the inheritance syntax in object-oriented thinking at all.

From another dimension, Go language also provides inheritance, but uses a compositional grammar, so we call it anonymous composition.

For example:

package main

import "fmt"

type Base struct {
    
    
   Name string
}

func (b *Base) SetName(name string) {
    
    
   b.Name = name
}

func (b *Base) GetName() string {
    
    
   return b.Name
}

// 组合,实现继承
type Child struct {
    
    
   base Base // 这里保存的是Base类型
}

// 重写GetName方法
func (c *Child) GetName() string {
    
    
   c.base.SetName("modify...")
   return c.base.GetName()
}

// 实现继承,但需要外部提供一个Base的实例
type Child2 struct {
    
    
   base *Base // 这里是指针
}

//
type Child3 struct {
    
    
   Base
}

type Child4 struct {
    
    
   *Base
}

func main() {
    
    
   c := new(Child)
   c.base.SetName("world")
   fmt.Println(c.GetName())

   c2 := new(Child2)
   c2.base = new(Base) // 因为Child2里面的Base是指针类型,所以必须提供一个Base的实例
   c2.base.SetName("ccc")
   fmt.Println(c2.base.GetName())

   c3 := new(Child3)
   c3.SetName("1111")
   fmt.Println(c3.GetName())

   c4 := new(Child4)
   c4.Base = new(Base)
   c4.SetName("2222")
   fmt.Println(c4.GetName())
}

Regarding this code, there are a few points that need to be explained

  1. Both anonymous and non-anonymous combinations can be used. If a non-anonymous combination is used, the specified variable name needs to be displayed when calling. If you use anonymous combination, you do not need to display the specified variable name, of course, you can also display the call, such as c3.Base.Name.
  2. In the Go language, it is also possible to "derive" from a type in the form of pointers: such as Child4, this Go code still has a "derived" effect, but when Child4 creates an instance, it needs to provide an external pointer to an instance of the Base class.
  3. When the "derived class" Child3 does not rewrite the member method of the "base class" Base, the corresponding method is "inherited". For example, in the above example, the effect of calling c3.GetName() and calling c3.Base.GetName() Unanimous. In the "derived class" Child rewrite the member method of the "base class" Base, c.GetName() will call the method of the derived class, if you want to call the method of the base class, you can display the call.
  4. If the "derived class" and "base class" have the same variable name Name, all access to the Name member of the derived class will only access the outermost Name variable, and the Name variable of the base class is equivalent to being overwritten. You can use the display reference.

Polymorphism

In the Go language, a class only needs to implement all the functions required by the interface, and we say that this class implements the interface. If the class implements the interface, the object instance can be assigned to the interface.

For example:

package main

import "fmt"

type Money interface {
    
    
   show() string
}

type OldMoney struct {
    
    
}

func (oldMoney *OldMoney) show() string {
    
    
   return "I am old money"
}

type NewMoney struct {
    
    
}

func (newMoney *NewMoney) show() string {
    
    
   return "I am new money"
}

func PrintMoney(l []Money) {
    
    
   for _, item := range l {
    
    
      fmt.Println(item.show())
   }
}

func main() {
    
    
   moneyList := []Money{
    
    new(OldMoney), new(NewMoney), new(OldMoney)}
   PrintMoney(moneyList)
}

Regarding this code, there are a few points that need to be explained:

  1. Interface assignment does not require that the two interfaces must be equivalent. If the method list of interface A is a subset of the method list of interface B, then interface B can be assigned to interface A.

  2. Go language can automatically generate a new show() method according to the following function: func (oldMoney OldMoney) show() string: func (oldMoney *OldMoney) show() string, so when assigning values, use references or objects, and function parameters need to be considered type.

in conclusion

A brief review of the characteristics of the Go language, using Go for object-oriented programming is extremely simple. We will implement 23 design patterns in Go later.

data

  1. Understanding and implementation details of encapsulation in Go language (Golang classic programming case)
  2. golang inheritance and interface
  3. golang inheritance
  4. The difference between Golang composition and anonymity
  5. 27. Polymorphism in golang

At last

If you like my article, you can follow my official account (Programmer Mala Tang)

My personal blog is: https://shidawuhen.github.io/

Review of previous articles:

  1. Go language
  2. MySQL/Redis
  3. algorithm
  4. Architecture/Network/Project
  5. Thinking/reading notes

Guess you like

Origin blog.csdn.net/shida219/article/details/113529395