设计模式6原则之:开闭原则

概念

一个软件实体如类、模块和函数应该对修改关闭,对扩展开放

理解

  1. 尽量减少对类文件的修改
  2. 通过另一个分支扩展其功能

实例

版本 1

需求

现在后台有课程小节编辑功能,打算增加一个小测验题型,并提供保存小测验的接口

方案

工厂方法:

package quizfactory

import "rabbitm/design/quizservice"

type IFactory interface {
	Create() quizservice.IQuiz
}

type QuizFactory struct {
}

func (f *QuizFactory) Create() quizservice.IQuiz {
	return new(quizservice.Quiz)
}

小测验接口:

package quizservice

import "fmt"

type IQuiz interface {
	SaveQuiz(params map[string]string)
}

type Quiz struct {
}

func (qz *Quiz) SaveQuiz(params map[string]string) {
	fmt.Println("保存小测验")
}

程序入口:

package main

import (
	"rabbitm/design/quizfactory"
	"rabbitm/design/quizservice"
)

var iQuiz quizservice.IQuiz

var iFactory quizfactory.IFactory

func main() {
	params := make(map[string]string)
	// 创建工厂
	iFactory = new(quizfactory.QuizFactory)
	// 创建小测验实例
	iQuiz = iFactory.Create()
	// 保存小测验
	iQuiz.SaveQuiz(params)
}

版本 2

需求

现在小测验需要引入新题型,之前默认的是单选,现在增加多选

方案

没有考虑开闭原则,直接在具体类内修改

小测验接口:

package quizservice

import "fmt"

type IQuiz interface {
	SaveQuiz(params map[string]string)
}

type Quiz struct {
}

func (qz *Quiz) SaveQuiz(params map[string]string) {
	// fmt.Println("保存小测验")
	if params["type"] == "单选" {
		fmt.Println("保存小测验单选")
	} else if params["type"] == "多选" {
		fmt.Println("保存小测验多选")
	}
}

结论

这种方式最容易想到,也是最好实现的,但是违反了开闭原则。如果小测验类型继续增多,会导致 SaveQuiz 方法变得臃肿难以维护

版本 3

需求

需要在方案 2 的基础上优化成符合开闭原则,对扩展开放,对修改关闭

方案

工厂方法:

package quizfactory

import "rabbitm/design/quizservice"

type IFactory interface {
	Create() quizservice.IQuiz
}

type QuizFactory struct {
}

func (f *QuizFactory) Create() quizservice.IQuiz {
	return new(quizservice.Quiz)
}

// 增加单选工厂
type QuizRadioFactory struct {
}

func (f *QuizRadioFactory) Create() quizservice.IQuiz {
	return new(quizservice.QuizRadio)
}

// 增加多选工厂
type QuizCheckboxFactory struct {
}

func (f *QuizCheckboxFactory) Create() quizservice.IQuiz {
	return new(quizservice.QuizCheckbox)
}

小测验接口:

package quizservice

import "fmt"

type IQuiz interface {
	SaveQuiz(params map[string]string)
}

type Quiz struct {
}

func (qz *Quiz) SaveQuiz(params map[string]string) {
	fmt.Println("保存小测验")
}

// 增加单选实现
type QuizRadio struct {
}

func (qz *QuizRadio) SaveQuiz(params map[string]string) {
	fmt.Println("保存单选题")
}

// 增加多选实现
type QuizCheckbox struct {
}

func (qz *QuizCheckbox) SaveQuiz(params map[string]string) {
	fmt.Println("保存多选题")
}

程序入口:

package main

import (
	"rabbitm/design/quizfactory"
	"rabbitm/design/quizservice"
)

var iQuiz quizservice.IQuiz

var iFactory quizfactory.IFactory

func main() {
	params := make(map[string]string)
	if params["type"] == "单选" {
		iFactory = new(quizfactory.QuizRadioFactory)
		iQuiz = iFactory.Create()
	} else if params["type"] == "多选" {
		iFactory = new(quizfactory.QuizCheckboxFactory)
		iQuiz = iFactory.Create()
	} else {
		iFactory = new(quizfactory.QuizFactory)
		iQuiz = iFactory.Create()
	}

	iQuiz.SaveQuiz(params)
}

结论

扩展了单选多选小测验,而没有修改原有的小测验代码。避免了更换维护人员和代码分支过多导致的意外