The previous article covered simple factories and factory methods. Linked to the Go Design Pattern (7)- Factory Pattern, this article takes a look at abstract factories. Abstract factory is not very common, mainly because the scene that abstract factory solves is relatively special, it is difficult to encounter in the actual development, but abstract factory provides the idea of reducing the number of classes, increasing the maintainability of the system, it is worth learning.

In this paper, a UML class diagram link is: www.processon.com/view/link/6…

This article is linked to: github.com/shidawuhen/…

Definition 1.

1.1 Abstract Factory

Abstract factory: Provides an interface to create a series of related or interdependent objects without specifying their concrete classes.

UML class diagram:

1.2 analysis

The UML class diagram shows the complexity of the abstract factory, which when broken down is not very different from the Factory method Go Design Pattern (7) -Factory pattern. Client is the caller, so you don’t have to worry about it. AbstractProductA and AbstractProductB exist, so if we remove AbstractProductB and its subclasses, is it the same as factory mode?

With the addition of AbstractProductB, the complexity increases. Instead of “define an interface for creating objects” in factory methods, the definition of an abstract factory is “provide an interface for creating a series of related objects.” A factory now has the ability to create multiple objects.

2. Application scenarios

Before the following explanation, LET me give you an example for easy explanation and understanding. There aren’t many examples of abstract factories, but I found one in The Big Talk Design Pattern, which is rare, but not impossible.

Suppose we do a student management system, the system has two tables, respectively is the student table and the score table, the student table has insert, update operation, the score table has insert, list operation. The system was built 20 years ago, the storage is using Access, now we plan to change to MySQL, in the future the school is rich, we plan to change to Oracle.

The factory model is not appropriate in this case for two reasons

  1. The Product returned by the factory pattern has the same parent class, and Product can be replaced at will because of polymorphism. But the student class and the grade class operation is not the same, forced to set up a parent class, is not reasonable.
  2. Even if a parent class is forced, four factories will be generated. If the number of tables (N) and storage types (M) increases, the number of factory classes will be N*M, resulting in a sharp increase in the number of factory classes.

If you look closely at this example, there are actually two dimensions:

The lower dimensions are the student class and the grade class, which correspond to AbstractProductA and AbstractProductB, respectively. The operations are the same regardless of the store, so you can use inheritance.

The higher dimension is the storage mode, because the storage mode contains the corresponding lower dimension student class and achievement class, namely ProductA1, ProductB1 or ProductA2, ProductB2.

Abstract Factory ConcreteFactory is a high-dimensional factory that represents a store that generates all low-dimensional classes in that store. So ConcreteFactory needs to generate the corresponding student class and achievement class.

In fact, after understanding the two dimensions, the idea of abstract factory is also easy to approach the factory method. As described in the Go Design Pattern (7)- Factory Pattern in the previous article, the implementation of the factory method is as follows: The factory method returns the named factory and then creates a ConcreteProduct from the named factory. The implementation of the abstract factory is as follows: the abstract factory first returns a higher-dimensional named factory, and then creates all lower-dimensional ConcreteProducts through that factory.

This design is reasonable. First of all, when the storage mode is replaced in the later stage, only the factories created need to be modified uniformly. Secondly, no matter adding low dimension (subject class, etc.) or adding storage mode, the change of factory class is very small. It only needs to add functions (such as adding low-dimension subject class) or factory class (such as adding high-latitude storage mode), and the Product class cannot get rid of the linear increase (after all, new functions need to be written).

3. Code implementation

package main

import "fmt"

/ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / the Product parts
/** * @description: the student interface defines the insert and update functions */
type Student interface {
   insert() bool
   update() bool
}

/** * @description: Access Student */
type AccessStudent struct{}/** * @description: Insert data into Student with Access * @receiver a * @return bool */
func (a *AccessStudent) insert(a) bool {
   fmt.Println("AccessStudent insert")
   return true
}

/** * @description: Update data to Student using Access * @receiver a * @return bool */
func (a *AccessStudent) update(a) bool {
   fmt.Println("AccessStudent update")
   return true
}

/** * @description: MySQL operates on Student */
type MySQLStudent struct{}/** * @description: insert data into Student table * @receiver a * @return bool */
func (m *MySQLStudent) insert(a) bool {
   fmt.Println("MySQLStudent insert")
   return true
}

/** * @description: update data from Student * @receiver a * @return bool */
func (m *MySQLStudent) update(a) bool {
   fmt.Println("MySQLStudent update")
   return true
}

/** * @description: insert and list functions */
type Score interface {
   insert() bool
   list() []int64
}

/** * @description: Access Score */
type AccessScore struct{}/** * @description: Use Access to insert data into Score * @receiver a * @return bool */
func (a *AccessScore) insert(a) bool {
   fmt.Println("AccessScore insert")
   return true
}

@receiver a * @return []int64 */
func (a *AccessScore) list(a) []int64 {
   fmt.Println("AccessScore list")
   return []int64{1.2}}/** * @description: MySQL Score */
type MySQLScore struct{}/** * @description: insert data into Score table * @receiver a * @return bool */
func (m *MySQLScore) insert(a) bool {
   fmt.Println("MySQLScore insert")
   return true
}

* @receiver a * @return []int64 */
func (m *MySQLScore) list(a) []int64 {
   fmt.Println("MySQLScore list")
   return []int64{1.2}}/ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / Factory
/** * @description: Abstract factory interface, representing high-dimensional factories, high-dimensional factories can generate low-dimensional objects */
type Factory interface {
   createStudent() Student
   createScore() Score
}

/** * @description: high dimension Access factory */
type AccessFactory struct{}/** * @description: High dimension Access factory, create Access Student object * @receiver a * @return Student */
func (a *AccessFactory) createStudent(a) Student {
   return &AccessStudent{}
}

/** * @description: High dimension Access factory, create Access Score object * @receiver a * @return Score */
func (a *AccessFactory) createScore(a) Score {
   return &AccessScore{}
}

/** * @description: high dimensional MySQL factory */
type MySQLFactory struct{}/** * @description: high dimension MySQL factory, create MySQL Student object * @receiver a * @return Student */
func (m *MySQLFactory) createStudent(a) Student {
   return &MySQLStudent{}
}

/** * @description: high dimension MySQL factory, create MySQL Score object * @receiver a * @return Score */
func (m *MySQLFactory) createScore(a) Score {
   return &MySQLScore{}
}

/ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / factory for high dimension
func getFactory(storeType string) Factory {
   switch storeType {
   case "MySQL":
      return &MySQLFactory{}
   case "Access":
      return &AccessFactory{}
   }
   return nil
}

func main(a) {
   // Abstract factory uses code
   fmt.Println("------------ Abstract Factory")
   factory := getFactory("MySQL")
   if factory == nil {
      fmt.Println("This storage mode is not supported")
      return
   }

   student := factory.createStudent()
   score := factory.createScore()

   student.insert()
   student.update()
   score.insert()
   score.list()
}
Copy the code

After you modify storeType, the storage mode will be automatically changed. If you set factory, student, and Score to global, you can easily change the storage mode by making changes at creation time.

Return to:

➜ myproject go run main.go

———— Abstract Factory

AccessStudent insert

AccessStudent update

AccessScore insert

AccessScore list

➜ myproject go run main.go

———— Abstract Factory

MySQLStudent insert

MySQLStudent update

MySQLScore insert

MySQLScore list

conclusion

Abstract factory pattern can reduce the number of factory class creation, it more embodies the idea of how to manage and classify information.

Abstract factories conform to the single responsibility principle (one class per table), the Richter substitution principle (inheritance is used so that subclass objects can replace superclass objects wherever they appear, whether factory or Product), and the dependency reversal principle (factory classes are associated with AbstractProduct classes, not details).

As for the open closed principle, adding new classes and new storage methods, the extension is open, and the location of the violation of the closure is in the getFactory function and Factory function, the former can be solved by using configuration files, the latter because it is the Go language, in fact, the Factory interface is only added to the function name, nothing else is changed, little change.

Do we need to use the abstract factory pattern when building similar functionality? My understanding is that patterns can be shelved to minimize the impact of patterns on developers.

Because change stores are extremely unlikely during the project life cycle, if multiple sets of stores are indeed supported, it means adding operations to a table and all stores need to add this function synchronously, which is too expensive to maintain. But it’s hard to say it’s not possible, so you can build a shelf and manage only one store.

As for the least impact on developers, it means that when used, developers can easily access table operation objects without having to understand the abstract factory pattern, which would be more than worth the loss. After all, reasonable design does not involve excessive design and cumbersome use.

The last

If you like my article, you can follow my public account (Programmer Malatang)

My personal blog is shidawuhen.github. IO /

Review of previous articles:

recruitment

  1. Bytes to beat | trill electric ShangWuHan server-side development engineer (senior)
  2. Bytes to beat | fly book big customer push product manager
  3. Bytes to beat | trill electricity service side technical posts vacant
  4. Bytedance recruitment special

Design patterns

  1. Go Design Pattern (8)- Abstract Factory
  2. Go Design Mode (7)- Factory Mode
  3. Go Design Pattern (6)- Singleton pattern
  4. Go Design Pattern (5)- Class diagram symbolic representation
  5. Go Design Pattern (4)- Code writing optimization
  6. Go Design Pattern (4)- Code writing
  7. Go Design Patterns (3)- Design principles
  8. Go Design Pattern (2)- Object-oriented analysis and design
  9. Go Design Pattern (1)- Syntax

language

  1. Go tool generate
  2. Go singleton implementation scheme
  3. Implementation principle of Go channel
  4. Implementation principle of Go timer
  5. Beego framework use
  6. Golang source BUG tracking
  7. Gin framework concise version
  8. Gin source code analysis

architecture

  1. Payment access general issues
  2. Current limiting 2
  3. Seconds kill system
  4. Distributed systems and consistency protocols
  5. Service framework and registry for microservices
  6. Discussion on Micro-service
  7. Current limiting implementation 1
  8. CDN request process details
  9. Common Cache tips
  10. How to effectively connect with third-party payment
  11. Algorithm is summarized

storage

  1. MySQL development specification
  2. Redis implements distributed locking
  3. The implementation principle of atomicity, consistency and persistence of transactions
  4. InnoDB locks and transactions

network

  1. HTTP2.0 basics tutorial
  2. HTTPS Configuration Combat
  3. HTTPS Connection Process
  4. TCP Performance Optimization

tool

  1. GoLand Practical skills
  2. Automatically generate go struct from mysql table
  3. Markdown editor recommends – Typora

Reading notes

  1. The principle of
  2. History As A Mirror
  3. Agile revolution
  4. How to exercise your memory
  5. Simple Logic – After reading
  6. Hot Wind – After reading
  7. Analects of Confucius – After reading
  8. Sun Tzu’s Art of War – Reflections from reading

thinking

  1. Struggle to mobilize all forces for victory
  2. Anti-liberalism
  3. practical
  4. The standard by which you judge yourself
  5. Service team holiday shift plan
  6. Project process management
  7. Some thoughts on project management
  8. Some thoughts on product manager
  9. Thinking about programmer career development
  10. Thinking about code review