适配器模式
作为两个不兼容的接口之间的桥梁,将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
比如:读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。
适配器继承或依赖已有的对象,实现想要的目标接口。
func main() {
player := AudioPlayer{}
player.play("mp3", "test1.mp3")
player.play("mp4", "test2.mp4")
player.play("wav", "test3.wav")
player.play("txt", "test4.txt")
player.play("png", "test5.png")
}
var mediaAdapter MediaAdapter
type Player interface {
play(audioType string, fileName string)
}
type AdvPlayer interface {
playMp4(fileName string)
playWAV(fileName string)
}
type Mp4Player struct {
}
type WAVPlayer struct {
}
func (receiver Mp4Player) playMp4(fileName string) {
fmt.Println("Mp4Player playing ... :", fileName)
}
func (receiver Mp4Player) playWAV(fileName string) {
}
func (receiver WAVPlayer) playMp4(fileName string) {
}
func (receiver WAVPlayer) playWAV(fileName string) {
fmt.Println("WAVPlayer playing ... :", fileName)
}
type MediaAdapter struct {
mp4 Mp4Player
wav WAVPlayer
}
func (receiver MediaAdapter) play(audioType string, fileName string) {
switch audioType {
case "mp4":
receiver.mp4.playMp4(fileName)
case "wav":
receiver.wav.playWAV(fileName)
}
}
type AudioPlayer struct {
}
func (receiver AudioPlayer) play(audioType string, fileName string) {
if audioType == "mp3" {
fmt.Println("Mp3Player playing ... :", fileName)
} else if audioType == "mp4" || audioType == "wav" {
mediaAdapter.play(audioType, fileName)
} else {
fmt.Println("不支持这种格式:", audioType)
}
}
/*
Mp3Player playing ... : test1.mp3
Mp4Player playing ... : test2.mp4
WAVPlayer playing ... : test3.wav
不支持这种格式: txt
不支持这种格式: png
假设 AudioPlayer 只能够播放 MP3,要播放 MP4、WAV可以通过扩展适配器去完成这些工作。
桥接模式
将抽象部分与实现部分分离,使它们都可以独立的变化
优点:1、抽象和实现的分离。 2、优秀的扩展能力。 3、实现细节对客户透明。
缺点:桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求针对抽象进行设计与编程。
假设有两台电脑,Mac和Windows。 还有两台打印机, 爱普生和惠普。 这两台电脑和打印机可能会任意组合使用。 客户端不应去担心如何将打印机连接至计算机的细节问题,创建两个层次结构,而不是2*2组合的结构。
看的见得计算机是抽象的,实施的打印机是具体的,两个层次通过桥接沟通,计算机包含对打印机的引用,抽象层和实施层可独立开发互不影响。
func main() {
hp := Hp{}
epson := Epson{}
mac := Mac{}
win := Win{}
mac.SetReqPrinter(hp)
mac.ReqPrint()
win.SetReqPrinter(epson)
win.ReqPrint()
}
type Computer interface {
ReqPrint()
SetReqPrinter(ReqPrinter)
}
type Mac struct {
ReqPrinter ReqPrinter
}
type Win struct {
ReqPrinter ReqPrinter
}
type ReqPrinter interface {
ReqPrintFile()
}
type Hp struct {
}
type Epson struct {
}
func (receiver Epson) ReqPrintFile() {
fmt.Println("爱普生 接受打印请求")
}
func (receiver Hp) ReqPrintFile() {
fmt.Println("惠普 接受打印请求")
}
func (receiver *Mac) ReqPrint() {
fmt.Println("Mac 请求打印机。。。")
receiver.ReqPrinter.ReqPrintFile()
}
func (receiver *Mac) SetReqPrinter(ReqPrinter ReqPrinter) {
receiver.ReqPrinter = ReqPrinter
}
func (receiver *Win) ReqPrint() {
fmt.Println("Win 请求打印机。。。")
receiver.ReqPrinter.ReqPrintFile()
}
func (receiver *Win) SetReqPrinter(ReqPrinter ReqPrinter) {
receiver.ReqPrinter = ReqPrinter
}
/*
Mac 请求打印机。。。
惠普 接受打印请求
Win 请求打印机。。。
爱普生 接受打印请求
过滤器模式
允许使用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来,它结合多个标准来获得单一标准。
func main() {
maleFilter = NewMaleFilter()
femaleFilter = NewFemaleFilter()
adultFilter = NewAdultFilter()
var people []Person
people = append(people, Person{name: "张三", sex: "男", age: 20})
people = append(people, Person{name: "王五", sex: "男", age: 8})
people = append(people, Person{name: "李四", sex: "女", age: 30})
people = append(people, Person{name: "赵六", sex: "女", age: 13})
male := maleFilter.Filter(people)
female := femaleFilter.Filter(people)
adult := adultFilter.Filter(people)
fmt.Printf("原始:%v\n", people)
fmt.Printf("男性:%v\n", male)
fmt.Printf("女性:%v\n", female)
fmt.Printf("成年:%v\n", adult)
}
var maleFilter *MaleFilter
var femaleFilter *FemaleFilter
var adultFilter *AdultFilter
type Person struct {
name string
sex string
age uint32
}
type Filter interface {
Filter([]Person) []Person
}
type MaleFilter struct {
}
type FemaleFilter struct {
}
type AdultFilter struct {
}
func (receiver MaleFilter) Filter(people []Person) []Person {
var tmp []Person
for _, item := range people {
if item.sex == "男" {
tmp = append(tmp, item)
}
}
return tmp
}
func (receiver FemaleFilter) Filter(people []Person) []Person {
var tmp []Person
for _, item := range people {
if item.sex == "女" {
tmp = append(tmp, item)
}
}
return tmp
}
func (receiver AdultFilter) Filter(people []Person) []Person {
var tmp []Person
for _, item := range people {
if item.age >= 18 {
tmp = append(tmp, item)
}
}
return tmp
}
func NewMaleFilter() *MaleFilter {
return new(MaleFilter)
}
func NewFemaleFilter() *FemaleFilter {
return new(FemaleFilter)
}
func NewAdultFilter() *AdultFilter {
return new(AdultFilter)
}
/*
原始:[{张三 男 20} {王五 男 8} {李四 女 30} {赵六 女 13}]
男性:[{张三 男 20} {王五 男 8}]
女性:[{李四 女 30} {赵六 女 13}]
成年:[{张三 男 20} {李四 女 30}]
组成模式
将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性,模糊了简单和复杂的概念,客户程序可以像处理简单元素一样来处理复杂元素,内部结构解耦。
优点: 1、高层模块调用简单。 2、节点自由增加。
缺点:在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。
树枝和树叶和树干的关系,部分-整体层次结构
func main() {
id_1 := NewEmployee("张三", "董事长")
id_2 := NewEmployee("李四", "经理")
id_3 := NewEmployee("王五", "保安队长")
id_4 := NewEmployee("赵六", "后勤部长")
id_5 := NewEmployee("小明", "保安")
id_6 := NewEmployee("小红", "服务员")
id_1.add(id_2) // 董事长管经理就行
id_2.add(id_3) // 经理管保安队长和后勤部长就行
id_2.add(id_4)
id_3.add(id_5) // 保安队长管保安就行
id_4.add(id_6) // 后勤部长管服务员就行
for _, item1 := range id_1.getLowerStaff() {
fmt.Println(id_1.department, "的下级员工:", item1.department)
for _, item2 := range item1.getLowerStaff() {
fmt.Println(item1.department, "的下级员工:", item2.department)
for _, item3 := range item2.getLowerStaff() {
fmt.Println(item2.department, "的下级员工:", item3.department)
}
}
}
}
type Employee struct {
name string
department string
lowerStaff []*Employee
}
func (receiver *Employee) add(employee *Employee) {
receiver.lowerStaff = append(receiver.lowerStaff, employee)
}
func (receiver *Employee) getLowerStaff() []*Employee {
return receiver.lowerStaff
}
func NewEmployee(name string, department string) *Employee {
return &Employee{
name: name,
department: department,
lowerStaff: nil,
}
}
/*
董事长 的下级员工: 经理
经理 的下级员工: 保安队长
保安队长 的下级员工: 保安
经理 的下级员工: 后勤部长
后勤部长 的下级员工: 服务员
装饰器模式
允许向一个现有的对象添加新的功能,同时又不改变其结构,装饰器模式相比生成子类更为灵活。
优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
缺点:多层装饰比较复杂。
一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃装到框里;这时画、玻璃和画框形成了一个物体。
func main() {
stickerDecorator := NewStickerDecorator(XiaoMi{})
stickerDecorator.Sticker()
stickerDecorator.Play()
fmt.Println("----------------------")
shellDecorator := NewShellDecorator(HuaWei{})
shellDecorator.SetShell()
shellDecorator.Play()
fmt.Println("----------------------")
decorator := NewShellDecorator(NewStickerDecorator(XiaoMi{}))
decorator.Play()
}
type Sticker interface { // 贴膜
Sticker()
}
type SetShell interface { // 套壳
SetShell()
}
type Phone interface { // 手机
Play()
}
type HuaWei struct {
}
type XiaoMi struct {
}
type StickerDecorator struct { // 装饰器
}
type ShellDecorator struct {
}
func (receiver HuaWei) Play() {
fmt.Println("使用的是华为手机。。。")
}
func (receiver XiaoMi) Play() {
fmt.Println("使用的是小米手机。。。")
}
func (receiver *ShellDecorator) SetShell() {
fmt.Println("正在套壳")
}
func (receiver *StickerDecorator) Sticker() {
fmt.Println("正在贴膜")
}
func (receiver *ShellDecorator) Play() {
fmt.Println("使用的是套壳了的手机")
}
func (receiver *StickerDecorator) Play() {
fmt.Println("使用的是贴膜了的手机")
}
func NewStickerDecorator(phone Phone) *StickerDecorator {
phone.Play()
return new(StickerDecorator)
}
func NewShellDecorator(phone Phone) *ShellDecorator {
phone.Play()
return new(ShellDecorator)
}
/*
使用的是小米手机。。。
正在贴膜
使用的是贴膜了的手机
----------------------
使用的是华为手机。。。
正在套壳
使用的是套壳了的手机
----------------------
使用的是小米手机。。。
使用的是贴膜了的手机
使用的是套壳了的手机
外观模式
为子系统中的一组接口提供一个统一的高层接口,这个接口使得这一子系统更加容易使用,简化客户端之间的接口。定义系统的入口,客户端不需要知道系统内部的复杂联系。
优点: 1、减少系统相互依赖。 2、提高灵活性。 3、提高了安全性。
缺点:不符合开闭原则,如果要改东西很麻烦,继承重写都不合适。
去医院看病,可能要去挂号、门诊、划价、取药,让患者觉得很复杂,如果提供窗口,只让窗口人员来处理就很方便。
或者像智能家居这种通过路由器一拖多来处理的,只要通过操作路由器就可以统一管理启动关闭什么的,体验就很好。
func main() {
terminalHouseKeeper = NewHouseKeeper()
terminalHouseKeeper.OnKTV()
terminalHouseKeeper.OnHome()
terminalHouseKeeper.OnCinema()
terminalHouseKeeper.OnSweep()
}
// 终端管家
var terminalHouseKeeper *TerminalHouseKeeper
// 灯
type Light struct {
}
func (receiver Light) open() {
fmt.Println("打开电灯。。。")
}
func (receiver Light) off() {
fmt.Println("关闭电灯。。。")
}
func (receiver Light) stageopen() {
fmt.Println("打开氛围灯。。。")
}
func (receiver Light) stageoff() {
fmt.Println("关闭氛围灯。。。")
}
// 空调
type AirConditioner struct {
}
func (receiver AirConditioner) open() {
fmt.Println("打开空调。。。")
}
func (receiver AirConditioner) off() {
fmt.Println("关闭空调。。。")
}
// 电视
type TV struct {
}
func (receiver TV) open() {
fmt.Println("打开电视。。。")
}
func (receiver TV) off() {
fmt.Println("关闭电视。。。")
}
// 音箱
type VoiceBox struct {
}
func (receiver VoiceBox) open() {
fmt.Println("打开音箱。。。")
}
func (receiver VoiceBox) off() {
fmt.Println("关闭音箱。。。")
}
// 扫地机器人
type SweepRobot struct {
}
func (receiver SweepRobot) open() {
fmt.Println("启动扫地机器人。。。")
}
func (receiver SweepRobot) off() {
fmt.Println("关闭扫地机器人。。。")
}
type TerminalHouseKeeper struct {
light Light
airConditioner AirConditioner
tv TV
sweepRobot SweepRobot
voiceBox VoiceBox
}
func (receiver TerminalHouseKeeper) OnKTV() {
fmt.Println("-------------------")
fmt.Println("准备切换到 KTV 模式")
receiver.light.off()
receiver.light.stageopen()
receiver.voiceBox.open()
}
func (receiver TerminalHouseKeeper) OnSweep() {
fmt.Println("-------------------")
fmt.Println("准备切换到 打扫 模式")
receiver.sweepRobot.open()
}
func (receiver TerminalHouseKeeper) OnHome() {
fmt.Println("-------------------")
fmt.Println("准备切换到 居家 模式")
receiver.light.open()
receiver.airConditioner.open()
}
func (receiver TerminalHouseKeeper) OnCinema() {
fmt.Println("-------------------")
fmt.Println("准备切换到 影院 模式")
receiver.light.off()
receiver.tv.open()
}
func NewHouseKeeper() *TerminalHouseKeeper {
return new(TerminalHouseKeeper)
}
/*
-------------------
准备切换到 KTV 模式
关闭电灯。。。
打开氛围灯。。。
打开音箱。。。
-------------------
准备切换到 居家 模式
打开电灯。。。
打开空调。。。
-------------------
准备切换到 影院 模式
关闭电灯。。。
打开电视。。。
-------------------
准备切换到 打扫 模式
启动扫地机器人。。。
享元模式
享元模式想到的就是常量池、数据库连接池、缓冲池等等,在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。
优点:大大减少对象的创建,降低系统的内存,使效率提高。
缺点:提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。
比如在线象棋比赛,100场比赛同时举行,就需要维护 32*100个棋子对象,但是棋盘、颜色、规则都是一样的,所以只需要把32个棋子对象抽象出来当做享元,可以极大节省空间。
func main() {
factory := new(PieceFactory)
factory.CreatePieces()
InitBoard("0001", "张三", "李四", factory)
InitBoard("0002", "王五", "赵六", factory)
}
type Piece struct { // 棋子
name string
color string
}
type Pos struct { // 坐标
x int64
y int64
}
type GamePiece struct {
piece *Piece //棋子指针
pos Pos //棋子位置
user string //玩家名
room string //房间名
}
type PieceFactory struct { // 棋子池,32个棋子信息
pieces []*Piece
}
func (p *Piece) String() string {
return fmt.Sprintf("%s %s", p.name, p.color)
}
func (g *GamePiece) String() string {
return fmt.Sprintf("%s (%d,%d)", g.piece, g.pos.x, g.pos.y)
}
func (f *PieceFactory) CreatePieces() { // 创建32个棋子
f.pieces = make([]*Piece, 32)
f.pieces[0] = &Piece{
name: "兵",
color: "red",
}
f.pieces[1] = &Piece{
name: "兵",
color: "black",
}
f.pieces[2] = &Piece{
name: "炮",
color: "red",
}
f.pieces[3] = &Piece{
name: "炮",
color: "black",
}
// 。。。。。
}
func (f *PieceFactory) GetPiece(id int64) *Piece {
return f.pieces[id]
}
func InitBoard(room string, u1 string, u2 string, factory *PieceFactory) {
fmt.Println("-----------------------")
fmt.Printf("创建房间号:%s,玩家: %s %s \n", room, u1, u2)
fmt.Println("初始化棋盘创建房间中。。。。")
fmt.Println("-----------------------")
piece1 := &GamePiece{
piece: factory.GetPiece(0),
pos: Pos{1, 6},
room: room,
user: u1,
}
fmt.Printf("玩家:%s的 %s \n", u1, piece1)
fmt.Println("......")
piece2 := &GamePiece{
piece: factory.GetPiece(3),
pos: Pos{1, 3},
room: room,
user: u2,
}
fmt.Printf("玩家:%s的 %s \n", u2, piece2)
fmt.Println("......")
}
/*
-----------------------
创建房间号:0001,玩家: 张三 李四
初始化棋盘创建房间中。。。。
-----------------------
玩家:张三的 兵 red (1,6)
......
玩家:李四的 炮 black (1,3)
......
-----------------------
创建房间号:0002,玩家: 王五 赵六
初始化棋盘创建房间中。。。。
-----------------------
玩家:王五的 兵 red (1,6)
......
玩家:赵六的 炮 black (1,3)
......
代理模式
为其他对象提供一种代理以控制对这个对象的访问
主要解决在直接访问对象时带来的问题,比如要访问的对象在远程的机器上,在面向对象系统中,有些对象由于某些原因,直接访问会给使用者或者系统结构带来很多麻烦,可以在访问此对象时加上一个对此对象的访问层。
func main() {
good1 := Goods{
Name: "电脑",
Fact: true,
}
good2 := Goods{
Name: "手机",
Fact: false,
}
good3 := Goods{
Name: "平板",
Fact: true,
}
japanShop := JapanShop{}
proxyShop1 := ProxyShopping(japanShop)
proxyShop1.Buy(good1)
usaShop := USAShop{}
proxyShop2 := ProxyShopping(usaShop)
proxyShop2.Buy(good2)
proxyShop2.Buy(good3)
}
type Goods struct {
Name string // 商品
Fact bool // 真假
}
type Shopping interface {
Buy(goods Goods)
}
type JapanShop struct {
}
type USAShop struct {
}
func (receiver JapanShop) Buy(goods Goods) {
fmt.Printf("--- 日本海外购 %s ---\n", goods.Name)
}
func (receiver USAShop) Buy(goods Goods) {
fmt.Printf("--- 美国海外购 %s ---\n", goods.Name)
}
// 海外代购
type OverSeaProxy struct {
shopping Shopping
}
func ProxyShopping(shopping Shopping) Shopping {
return OverSeaProxy{
shopping: shopping,
}
}
func (receiver OverSeaProxy) Buy(goods Goods) {
if receiver.Identify(goods) {
receiver.shopping.Buy(goods)
}
}
func (receiver OverSeaProxy) Identify(goods Goods) bool {
fmt.Printf("--- 鉴别真伪 %s ---\n", goods.Name)
if goods.Fact == false {
fmt.Println(goods.Name, "是假货。。。")
}
return goods.Fact
}
/*
--- 鉴别真伪 电脑 ---
--- 日本海外购 电脑 ---
--- 鉴别真伪 手机 ---
手机 是假货。。。
--- 鉴别真伪 平板 ---
--- 美国海外购 平板 ---