【Day 27】Design Patterns with Go I:Simple Factory / Factory / Abstract Factory

刚学一点 Go,
除了能够使用别人写好的 modules,
为了程序码的可扩充性,也需要理解 Design Patterns。
今天搭配 Github 上的程序码来学习各种「工厂」的设计模式~

Design Patterns

大一刚学 Java 时,学到一些 interface 的概念,
也接触到 Design Patterns 的书,
但实在难以理解制造出会叫的鸭子跟未来写网页、写应用程序的关联?

设计模式,是别人研究出来,怎麽样写程序能达到好扩充的方法。
因为需求会变动,
如果新增一个功能就要增加非常多行程序码,
或是新增程序码常常导致旧功能出错,
十分浪费时间心力。

我觉得学习 Design Patterns 不是一件容易的事,
运用到很多抽象化的概念。
而就算理解一个设计模式,
接下来要能够融会贯通的使用在适合的情境,除了经验也需要刻意留心。

本篇参考这个 Github repo 的程序码:Go 语言设计模式 来理解设计模式。
而这本 Gitbook 设计模式学习笔记 讲解前面几个模式也算是简单易懂,并附上类别图和程序码,值得参考。

注意 Go 里面没有 object / class,
这边如果提到物件通常是 Go 中 implement interface 的 struct

Simple Factory

根据传入的参数回传指定物件

package main
import "fmt"

type Animal interface {
	Say() string
}

func newAnimal(t int) Animal {
	// 如果想增加不同动物,需要在这里改
	// 不符合 SOLID 的 O(开放封闭原则)
	if t == 1 {
		return Dog{}
	} else if t == 2{
		return &Cat{}
	}
	return nil
}
type Cat struct {}
type Dog struct {}

func (*Cat) Say() string {
	return "meow!"
}

func (Dog) Say() string {
	return "bark!"
}

func main() {
	a1 := newAnimal(1)
	fmt.Println("type 1 Animal")
	fmt.Println(a1.Say())
	a2 := newAnimal(2)
	fmt.Println("type 2 Animal")
	fmt.Println(a2.Say())	
}

Factory Pattern

规定一个工厂该有的样子(interface),要能够制造不同商品(instance)就各自去实现不同的工厂

repo 中的程序码以 Operator 来举例。04_factory_method

  • 情境举例:对於一个二元运算子(加减乘除等)来说,都是对於两个数字做操作的结果。把不同二元运算子都有的资料(两个数字)和方法(设定A、设定B、AB透过运算子得到的结果)抽象出来,定义新的二元运算子时不容易出错。

OperatorFactory 是工厂 interface,合格的工厂应该要实作一个会制造出 OperatorCreate method。
Operator 则是定义了 SetA SetB Result 三个 method。
SetA SetB 都是对 struct OperatorBase 定义的 method,OperatorBase 中有 a b 两整数。

提醒 Go 中没有继承,就用 nested struct 达到类似效果:

type PlusOperator struct {
	*OperatorBase
}

Abstract Factory

工厂产生的物件「有关联」

完整程序码:05_abstract_factory

  • 情境举例:要存订单一定要有主纪录与详情纪录。但有时可能用 RDB(指的是关连式资料库),有时候想存 XML。

Order interface

DAOFactory 是抽象工厂的 interface,要实作这个工厂,底下就要使用 CreateOrderMainDAO()CreateOrderDetailDAO()分别实作两个子工厂 OrderMainDAOOrderDetailDAO
这里就存有订单主纪录与详情纪录的抽象关系。

type OrderMainDAO interface {
	SaveOrderMain()
}
type OrderDetailDAO interface {
	SaveOrderDetail()
}

type DAOFactory interface {
	CreateOrderMainDAO() OrderMainDAO
	CreateOrderDetailDAO() OrderDetailDAO
}

RDB 工厂实作

RDB 工厂的实作环节,
首先先做一个 RDB 抽象工厂,
里面要去实作 使用 RDB 储存主纪录和详情纪录的实现方式。

XML 的工厂实作也一样,就是 method 里面的实作细节不同而已

// 使用 RDB 存主纪录
type RDBMainDAO struct{}
func (*RDBMainDAO) SaveOrderMain() {
	fmt.Print("rdb main save\n")
}
// 使用 RDB 存详情纪录
type RDBDetailDAO struct{}
func (*RDBDetailDAO) SaveOrderDetail() {
	fmt.Print("rdb detail save\n")
}

// RDB 的抽象工厂与实现
type RDBDAOFactory struct{}

func (*RDBDAOFactory) CreateOrderMainDAO() OrderMainDAO {
	return &RDBMainDAO{}
}
func (*RDBDAOFactory) CreateOrderDetailDAO() OrderDetailDAO {
	return &RDBDetailDAO{}
}

<<:  Day 27 - 3D绘图篇 - 2D图片上面的3D物件是怎麽产生的? I - 成为Canvas Ninja ~ 理解2D渲染的精髓

>>:  Day 28 - WooCommerce: 显示虚拟帐号付款资讯

[Day 14] 阿嬷都看得懂的 style 标签怎麽用

阿嬷都看得懂的 style 标签怎麽用 昨天我们介绍了 CSS 选择器,所以终於知道该怎麽把独立收整...

Day 0xE UVa10812 Beat the Spread!

Virtual Judge ZeroJudge 题意 输入比赛的分数总和及分差,输出两队分数 需要...

JavaScript型别、物件与纯值

JavaScript型别 前面有说过JavaScript是动态型别,也就是说在执行时,变数会依照赋予...

树选手3号:XGboost [python实例]

照着前几天的逻辑今天来用python执行xgboost,刚开始一样先写score function方...

2021 资通讯高峰论坛 !

https://edm.bnext.com.tw/2021cpx/?utm_source=%E3%8...