This is a creation in Article, where the information may have evolved or changed.
I am a designer of the design model, I believe a good architecture can give the stability of the system and later maintenance to bring great convenience, because recently there is time to re-learn GOF design patterns, resulting in the use of go to achieve the GOF classic design model ideas.
This article follows the context of the Gof book, which is the first in this series: The combination mode (Composite), which should be updated once a week if normal work permits. Welcome everyone to visit my blog, the code can be downloaded in @zuozuohao.
Gof in the second chapter through the design of a Lexi document editor to introduce the use of design patterns, gof that Lexi design faces seven problems:
1. Document Structure
2. Formatting
3. retouching the user interface
4. support a variety of visual sense
5. Support multiple window systems
6. User Action
7. spell checking and hyphens
Gof that Lexi documents are processed only for characters, lines, polygons, and other graphic elements. But Lexi users typically face the physical structure of the document's rows, columns, graphs, tables, and other sub-structures, which also have their own sub-structures.
The Lexi user interface should allow direct manipulation of these sub-structures, such as the ability of the user to manipulate the chart structure directly, to refer to, move, and so on, rather than as a bunch of text and graphics.
So Lexi internally says it should support
1. Maintain the physical structure of the document, arrange text and icons to rows, columns, tables, and so on.
2. Visualize the creation and display of documents.
3. Map the elements represented within the document according to the display location.
Gof that, first of all, text and graphics should be treated consistently, such as allowing users to embed text in a graphic, and vice versa.
Second, you should not emphasize the difference between a single element and an element group, and Lexi should treat simple tuples and combined elements consistently.
Finally, if you consider the subsequent addition of grammar analysis, the requirements for simple and combined elements conflict with the second, because the grammatical analysis of simple elements and composition elements is different (so design patterns need to be weighed).
Recursive combination
Gof uses recursive composition (Recursive composition) to represent the hierarchical structure of the Lexi entity, first arranging the characters and graphics from left to right into a single line of documents, then grouping multiple rows into one column, and finally composing multiple columns into one page, as shown in.
Gof describes this hierarchy by representing each important element as an object. These objects include not only visible elements such as characters, graphics, but also structured elements such as rows and columns, as shown in the structure of the object.
Entities
GOF defines an abstract entity (Glyph) for all the structures of a Document object. His subclasses define basic graphical elements (characters and images, etc.), and also include structured elements (rows and columns), and the inheritance structure of the classes as shown.
The following table describes the basic interfaces of the glyph.
| responsibity |
Operations |
| Appearance |
Virtual Void Draw (window*) |
|
Virtual Void Bounds (rect&) |
| Hit detection |
Virtual bool intersects (Const point&) |
| Structure |
Virtual Void Insert (glyph*, int) |
|
Virtual Void Remove (glyph*) |
|
Virtual Void Remove (glyph*) |
|
Virtual glyph* Child (int) |
|
Virtual glyph* Parent (int) |
Primitives have three responsibilities, 1) how they draw themselves, 2) how much space they occupy, 3) what their parent entities and sub-primitives are.
Glyph subclass to render itself on a window, the draw method of the parent class glyph must be overridden to render itself on the screen window.
The bounds method returns the rectangular area occupied by the entity, and the glyph subclass needs to override the method because each object occupies a different area.
Intersects determines whether a specified point intersects with an entity, to determine the entity or entity structure of the user's click position in the Lexi interface.
The Remove method moves the sub-entities of an object.
The child method returns the sub-entities of the given entity.
The parent method returns the Parented entity of the object.
The above is gof about the basic design that the Lexi document editor should follow, which should be summed up in two points:
1. Hierarchical object structure, including basic and composite elements
2. General-Purpose Interface design
Let's try to use Golang to implement this basic design pattern.
Golang Primitive Types
Lexi Document editor should include the meta type character, Rectangle, row and column, etc., in order to facilitate reading (mainly really do not want to hit so many words) we only choose character, Rectangle, row three objects to implement, Other types of elements can be tried on their own.
Limited to the length of reason (actually I really do not want to code word, hey) here just select a part of the GOF definition of the elements and interfaces, please understand.
Golang Primitive Type Interface Implementation *
As the class diagram has designed, all three include the draw and intersects methods, and the composite entity row has an Insert interface that inserts a sub-entity.
So we designed a generic Appearancer interface to describe the common interface type, with the following code:
type Appearancer interface { Draw(elemet Appearancer) Intersect(point int) SetParent(parentID int)}
In addition to having a name attribute, an entity should have an ID that identifies the identity to distinguish different entities, so the glyph, Character, Rectangle, and row types are designed as follows:
type glyph struct Span class= "Hljs-container" >{name string position int id int //id must > 0 parentid int //if parentid equal 0, the Span class= "Hljs-type" >glyph has no parents } type Character struct { Glyph} type Rectangle struct { Glyph} type Row struct { GlyphChilds []appearancer}
The following is the implementation part of the Appearancer interface, where the work of the common interface can be done in the glyph type:
Func (g *glyph) Draw (Elemet appearancer) {FMT. Println("I am a", reflect. TypeOf(Elemet),":"G. Name)}func (g *glyph) Intersect (point int) {if G. Position= = Point {FMT. Println(g. Name," is far away from", point)} else {FMT. Println(g. Name,"intersect with", point)}}func (G *glyph) SetParent (parentid int) {g. ParentID= Parentid}func (R *row) Insert (child appearancer, position int) {index: = r. Insertinrightplace(Child, position) child. SetParent(r. ID) FMT. Println("Add", Child,"to Childs at position", index) FMT. Println(r. Name,"' s length is", Len (r. Childs))}func (parent *row) insertinrightplace (child appearancer, position int) int {insertedposition: =0Childslength: = Len (parent. Childs) If position > (Childslength-1) {Parent. Childs= Append (Parent. Childs, child) Insertedposition = Childslength} else {Parent. Childs= Append (Parent. Childs[Position:position], child) insertedposition = position} return insertedposition}
Then you can insert the element directly into the row, the code is as follows:
func main() { c1 := &Row{Glyph{"c1"1210}, []Appearancer{}} c1.Draw(c1) c1.Intersect(2) c1.Insert(&Character{Glyph{"c1"12203) fmt.Println("hello Composite")}
Output:
I am a *main.Row : c1c1 with 2Add 1221toat position 0lengthis 1hello Composite
(You can try it here: Https://play.golang.org/p/9Cc6HwIqcO)
In fact, this is a very simple composite, there are a lot of places need to be perfect, for example, we need a global variable to take the ID array of stored elements, as well as the correct initialization of the rules and so on. But the basic skeleton about composite should be here, if the conditions allow me to refine these aspects later.
Thank you very much for reading this lengthy article, if there are errors please point out, I will revise as soon as possible, thank you!