设计模式说明
设计模式(Design Patterns)是在软件设计中经常使用的最佳实践的描述,它们是经过多年的实践和验证的。下面是一些常见的设计模式:
创建型模式(Creational Patterns)
- 单例模式(Singleton)
保证一个类只有一个实例。
- 工厂模式(Factory)
将对象的创建过程交给子类去实现。
- 抽象工厂模式(Abstract Factory)
提供一个接口用于创建一系列相关的对象。
- 建造者模式(Builder)
分步创建复杂对象。
- 原型模式(Prototype)
通过克隆创建新对象。
结构型模式(Structural Patterns)
- 适配器模式(Adapter)
将不兼容的接口转换成兼容的接口。
- 桥接模式(Bridge)
将抽象部分和具体实现部分分离。
- 组合模式(Composite)
统一处理单个对象和组合对象。
- 装饰器模式(Decorator)
动态地给对象添加新的功能。
- 外观模式(Facade)
简化复杂系统的接口。
- 享元模式(Flyweight)
共享细粒度的对象,以节省内存。
- 代理模式(Proxy)
通过代理控制对象的访问。
行为型模式(Behavioral Patterns)
- 职责链模式(Chain of Responsibility)
请求沿处理链传递,避免耦合。
- 命令模式(Command)
封装请求对象,解耦发送和接收。
- 解释器模式(Interpreter)
解释执行语言语句。
- 迭代器模式(Iterator)
遍历集合元素。
- 中介者模式(Mediator)
解耦多个对象之间的交互关系。
- 备忘录模式(Memento)
保存对象状态。
- 观察者模式(Observer)
当对象状态发生变化时,通知它的依赖者。
- 状态模式(State)
根据对象的状态,改变对象的行为。
- 策略模式(Strategy)
定义一系列算法,封装每个算法,并使它们可以互换。
- 模板方法模式(Template Method)
定义一个算法的骨架,具体实现由子类完成。
- 访问者模式(Visitor)
封装对数据结构的操作。
以上是常见的设计模式,每个模式都有自己的特点和应用场景。在软件设计中,根据需要选择适当的设计模式可以提高代码的质量、可维护性和可扩展性。
模式详解
单例模式(Singleton)
保证一个类只有一个实例。
// 它确保一个类只能被实例化一次,从而保证系统中只存在一个该类的实例。
object Singleton {
init {
// 在这里初始化单例对象
}
fun doSomething() {
// 这里是单例对象的方法
}
}
- 提供一个全局唯一的访问点:使用单例模式可以确保系统中只存在一个实例对象,从而提供一个全局唯一的访问点,使得其他对象能够方便地访问该实例。
- 控制对象的生成:由于单例模式只能生成一个实例对象,因此可以对实例对象的生成过程进行严格控制,确保系统中只有一个实例对象,并且该对象在系统中始终存在。
- 节省系统资源:由于单例模式只生成一个实例对象,因此可以节省系统资源,避免创建过多的对象导致系统负担过重。
- 实现数据共享:单例模式可以实现数据共享,即多个对象共享同一个实例对象的数据,从而避免了数据不一致的问题。
- 简化系统调用:由于单例模式提供了一个全局唯一的访问点,因此可以简化系统调用,避免了频繁地创建和销毁对象所带来的系统开销。
工厂方法模式(Factory Method Pattern)
将对象的创建过程交给子类去实现。
interface Exporter {
fun export(data: String): String
}
class PDFExporter : Exporter {
override fun export(data: String): String {
return "Exporting data to PDF format: $data"
}
}
class CSVExporter : Exporter {
override fun export(data: String): String {
return "Exporting data to CSV format: $data"
}
}
interface ExporterFactory {
fun createExporter(): Exporter
}
class PDFExporterFactory : ExporterFactory {
override fun createExporter(): Exporter {
return PDFExporter()
}
}
class CSVExporterFactory : ExporterFactory {
override fun createExporter(): Exporter {
return CSVExporter()
}
}
- 封装对象的创建:将对象的创建封装在工厂类中,使得客户端无需关注具体对象的创建过程。
- 实现对象的多态性:通过定义抽象工厂和具体工厂,实现了对象的多态性,客户端可以通过工厂接口来获取不同的具体对象。
- 对象创建和使用分离:工厂方法模式将对象的创建和使用分离开来,降低了客户端代码的复杂性,客户端只需要关注如何使用对4. 象,而无需了解对象的创建过程。
- 灵活性和可维护性:工厂方法模式可以通过增加或修改具体工厂来创建不同的对象,而不需要修改客户端代码,增加了系统的灵6. 活性和可维护性。
- 开闭原则:工厂方法模式可以通过增加新的具体工厂来创建新的对象,而不需要修改已有的代码,实现了对扩展开放,对修改关闭的开闭原则。
- 单一职责原则:将对象的创建职责分离出去,让每个类都只负责一件事情,遵循了单一职责原则。
抽象工厂模式(Abstract Factory)
提供一个接口用于创建一系列相关的对象。
// 定义抽象工厂接口
interface AbstractFactory {
fun createProduct(): Product
}
// 定义具体工厂1,实现抽象工厂接口
class ConcreteFactory1 : AbstractFactory {
override fun createProduct(): Product {
return ConcreteProduct1()
}
}
// 定义具体工厂2,实现抽象工厂接口
class ConcreteFactory2 : AbstractFactory {
override fun createProduct(): Product {
return ConcreteProduct2()
}
}
// 定义抽象产品接口
interface Product {
fun operation()
}
// 定义具体产品1,实现抽象产品接口
class ConcreteProduct1 : Product {
override fun operation() {
println("Product 1 operation")
}
}
// 定义具体产品2,实现抽象产品接口
class ConcreteProduct2 : Product {
override fun operation() {
println("Product 2 operation")
}
}
// 客户端代码
fun main() {
val factory1 = ConcreteFactory1()
val product1 = factory1.createProduct()
product1.operation()
val factory2 = ConcreteFactory2()
val product2 = factory2.createProduct()
product2.operation()
}
- 封装一组相互关联的产品对象创建:抽象工厂模式能够将一组相互关联的产品对象的创建封装在一起,使得客户端可以通过接口访问它们,而不必知道它们的具体实现细节。这样就能够将产品对象的创建和客户端代码分离开来,降低了客户端代码的复杂度,提高了代码的可维护性。
- 便于替换产品族:抽象工厂模式能够让客户端使用不同的具体工厂来创建不同的产品族,从而实现对产品族的替换。这样就能够灵活地改变系统中的产品组合,满足不同的需求。
- 保证产品对象的一致性:抽象工厂模式能够保证一个工厂创建的所有产品对象都属于同一产品族,这样就能够保证这些产品对象之间的一致性,从而避免了不同产品之间的兼容性问题。
- 增加新的产品族方便:抽象工厂模式能够方便地增加新的产品族,只需要增加新的具体工厂和新的具体产品即可。这样就能够让系统具有良好的扩展性和可维护性。
- 不容易增加新的产品等级结构:抽象工厂模式对于增加新的产品等级结构比较困难,因为需要修改所有的具体工厂类。如果需要增加新的产品等级结构,最好的方式是采用工厂方法模式或者简单工厂模式。
建造者模式(Builder)
分步创建复杂对象。
class Person(
val name: String,
val age: Int,
val gender: String,
val height: Int,
val weight: Int
) {
override fun toString(): String {
return "Name: $name, Age: $age, Gender: $gender, Height: $height cm, Weight: $weight kg"
}
}
class PersonBuilder(
var name: String = "",
var age: Int = 0,
var gender: String = "",
var height: Int = 0,
var weight: Int = 0
) {
fun setName(name: String): PersonBuilder {
this.name = name
return this
}
fun setAge(age: Int): PersonBuilder {
this.age = age
return this
}
fun setGender(gender: String): PersonBuilder {
this.gender = gender
return this
}
fun setHeight(height: Int): PersonBuilder {
this.height = height
return this
}
fun setWeight(weight: Int): PersonBuilder {
this.weight = weight
return this
}
fun build(): Person {
return Person(name, age, gender, height, weight)
}
}
- 分离构建过程:将一个复杂对象的构建过程分解为多个简单的构建步骤,使得不同的构建者可以重用相同的构建过程来构建不同的对象实例。
- 封装构建细节:将构建过程中的细节封装在一个独立的建造者对象中,使得客户端代码无需了解对象的构建过程,从而提高了代码的可维护性和可扩展性。
- 实现构建复杂对象:通过组合不同的构建步骤,建造者模式可以实现构建复杂对象,而不需要暴露对象的内部结构和细节。
- 支持不同的表示方式:建造者模式可以通过不同的构建者对象来实现不同的表示方式,例如不同的组装方式、不同的配置选项等。
- 灵活性和可扩展性:由于建造者模式将构建过程封装在一个独立的对象中,因此可以很容易地添加新的构建者对象来支持新的对象表示方式,从而提高了系统的灵活性和可扩展性。
原型模式(Prototype)
通过克隆创建新对象。
data class SimplePrototype(var value: Int) : Cloneable {
public override fun clone(): SimplePrototype {
return super.clone() as SimplePrototype
}
}
fun main() {
// 创建原型对象
val prototype = SimplePrototype(10)
// 克隆原型对象
val clone = prototype.clone()
// 打印克隆对象的值
println(clone.value) // 输出:10
}
- 克隆对象:通过克隆现有对象来创建新的对象实例,避免了重新创建对象所需的初始化过程,提高了性能。
- 简化对象创建:原型模式使得对象创建变得简单,只需复制现有对象即可,无需了解其内部实现。
- 支持动态配置对象:通过克隆现有对象并修改其属性,可以方便地配置新的对象。
- 避免对原型对象的修改:由于克隆的对象是一个新的对象实例,因此可以避免对原型对象进行修改而影响到其他对象。
- 隐藏对象创建细节:原型模式将对象创建过程封装在原型对象内部,隐藏了对象创建的细节,使得客户端代码更加简洁。
需要注意的是,在使用原型模式时,被克隆的对象必须实现 Cloneable 接口,并且重写 clone() 方法。此外,由于克隆出的对象是一个新的对象实例,因此需要注意处理对象引用的问题,避免出现对象状态不一致的情况。
适配器模式(Adapter)
将不兼容的接口转换成兼容的接口。
interface Target {
fun request(): String
}
class Adaptee {
fun specificRequest(): String {
return "Adaptee specific request"
}
}
class Adapter(private val adaptee: Adaptee) : Target {
override fun request(): String {
return adaptee.specificRequest()
}
}
fun main() {
// 创建 Adaptee 对象
val adaptee = Adaptee()
// 创建 Adapter 对象,并将 Adaptee 对象传递给它
val adapter = Adapter(adaptee)
// 调用 Target 的 request() 方法,实际上会调用 Adaptee 的 specificRequest() 方法
val result = adapter.request()
// 输出结果
println(result) // 输出:Adaptee specific request
}
- 连接不兼容的接口:适配器模式主要用于连接两个不兼容的接口,使它们能够协同工作。适配器作为一个中间层,通过转换一种接口调用到另一种接口的方式,使得两种不同的接口可以在一起协同工作。
- 提供不同的接口封装:适配器模式可以对现有的接口进行封装,提供不同的接口给客户端使用。这种封装可以屏蔽掉底层系统的实现细节,提供简洁易用的接口给客户端使用。
- 改变接口行为:适配器模式可以改变接口的行为,使得原本不能直接使用的接口变得可以直接使用。例如,适配器可以通过对参数的转换,改变接口的行为,使得客户端可以直接使用该接口。
- 对现有系统进行重构和扩展:适配器模式可以对现有的系统进行重构和扩展。例如,当一个新的系统需要与现有系统进行协作时,可以通过适配器模式将其接口转换为现有系统接口,从而实现协作。此外,当现有系统的接口需要修改时,可以通过适配器模式进行扩展,而不需要修改现有系统的代码。
桥接模式(Bridge)
将抽象部分和具体实现部分分离。
// 实现化角色
interface Implementor {
fun operationImpl()
}
// 具体实现化角色
class ConcreteImplementorA : Implementor {
override fun operationImpl() {
println("具体实现A的方法执行")
}
}
class ConcreteImplementorB : Implementor {
override fun operationImpl() {
println("具体实现B的方法执行")
}
}
// 抽象化角色
abstract class Abstraction(private val implementor: Implementor) {
abstract fun operation()
fun getImplementor(): Implementor {
return implementor
}
}
// 扩展抽象化角色
class RefinedAbstraction(implementor: Implementor) : Abstraction(implementor) {
override fun operation() {
println("扩展抽象化RefinedAbstraction调用:")
getImplementor().operationImpl()
}
}
// 客户端
fun main() {
val implementorA = ConcreteImplementorA()
val abstraction = RefinedAbstraction(implementorA)
abstraction.operation()
val implementorB = ConcreteImplementorB()
val abstractionB = RefinedAbstraction(implementorB)
abstractionB.operation()
}
- 分离抽象与实现:桥接模式通过将抽象化和实现化分离,使它们可以独立变化,而不相互影响。这样可以使抽象化角色和实现化角色可以分别进行修改和扩展,而不需要修改彼此的代码。
- 提高可扩展性:桥接模式可以通过增加实现化角色或抽象化角色来扩展系统。例如,可以通过添加一个新的实现化角色来增加系统的功能,而不需要修改现有的代码。
- 对系统进行解耦:桥接模式可以将抽象化角色和实现化角色分离,从而将系统的各个部分进行解耦。这样可以使得系统的不同部分可以独立变化,而不会相互影响。
- 实现细节对客户端不透明:桥接模式可以将实现化角色的细节对客户端进行屏蔽,使得客户端只需要关心抽象化角色的接口,而不需要关心实现化角色的具体实现。这样可以使得客户端代码更加简洁清晰。
组合模式(Composite)
统一处理单个对象和组合对象。
// 组合模式中的抽象构件
interface Component {
fun operation()
}
// 叶子构件
class Leaf(private val name: String) : Component {
override fun operation() {
println("Leaf $name:被访问!")
}
}
// 容器构件
class Composite(private val name: String) : Component {
private val children = mutableListOf<Component>()
override fun operation() {
println("Composite $name:被访问!")
for (child in children) {
child.operation()
}
}
fun add(component: Component) {
children.add(component)
}
fun remove(component: Component) {
children.remove(component)
}
fun getChild(index: Int): Component {
return children[index]
}
}
// 客户端
fun main() {
val root = Composite("root")
val leaf1 = Leaf("leaf1")
val leaf2 = Leaf("leaf2")
val composite1 = Composite("composite1")
val leaf3 = Leaf("leaf3")
val leaf4 = Leaf("leaf4")
val composite2 = Composite("composite2")
val leaf5 = Leaf("leaf5")
root.add(leaf1)
root.add(leaf2)
composite1.add(leaf3)
composite1.add(leaf4)
composite2.add(leaf5)
root.add(composite1)
root.add(composite2)
root.operation()
}
- 将对象组合成树形结构:组合模式可以将一组对象组合成树形结构,以表示"整体-部分"的层次结构。这样可以方便地处理对象之间的关系,并能够快速地定位到需要的对象。
- 统一对待单个对象和组合对象:组合模式中,单个对象和组合对象被统一对待。这意味着客户端代码可以像处理单个对象一样处理组合对象,而不需要关心它们的具体实现细节。
- 简化客户端代码:组合模式可以让客户端代码更加简洁和清晰。客户端只需要与抽象的组件类打交道,而不需要关心组件的具体实现。这样可以降低代码的耦合度,提高代码的可维护性。
- 实现了递归组合:组合模式支持递归组合。也就是说,组合对象可以包含其他组合对象,从而形成一个复杂的树形结构。这样可以处理复杂的层次关系,使得系统更加灵活和可扩展。
装饰器模式(Decorator)
动态地给对象添加新的功能。
// 抽象组件
interface Component {
fun operation()
}
// 具体组件
class ConcreteComponent : Component {
override fun operation() {
println("执行具体组件的操作")
}
}
// 抽象装饰器
abstract class Decorator(private val component: Component) : Component {
override fun operation() {
component.operation()
}
}
// 具体装饰器A
class ConcreteDecoratorA(component: Component) : Decorator(component) {
private val addedState = "New State"
override fun operation() {
super.operation()
println("具体装饰器A的操作")
addedBehavior()
}
private fun addedBehavior() {
println("ConcreteDecoratorA增加的行为$addedState")
}
}
// 具体装饰器B
class ConcreteDecoratorB(component: Component) : Decorator(component) {
override fun operation() {
super.operation()
addedBehavior()
}
private fun addedBehavior() {
println("具体装饰器B的操作")
}
}
// 客户端
fun main() {
val component = ConcreteComponent()
val decoratorA = ConcreteDecoratorA(component)
val decoratorB = ConcreteDecoratorB(decoratorA)
decoratorB.operation()
}
- 扩展原有对象的功能:装饰器模式允许在不改变原有对象的情况下,动态地为其添加新的行为或功能。
- 灵活性和可组合性:装饰器模式中的各个组件都可以灵活组合,且可以随时添加或删除装饰器,从而实现各种不同的组合方式和功能。
- 保持接口一致性:装饰器模式通过继承共同的接口,使得装饰器与原有对象具有相同的接口,从而保持了对象之间的兼容性和一致性。
外观模式(Facade)
简化复杂系统的接口。
// 外观类
class CarFacade {
private val engine = Engine()
private val gearbox = Gearbox()
private val brakes = Brakes()
fun start() {
engine.start()
gearbox.shiftGear(1)
brakes.release()
}
fun stop() {
gearbox.shiftGear(0)
brakes.apply()
engine.stop()
}
}
// 子系统类
class Engine {
fun start() {
println("Engine started.")
}
fun stop() {
println("Engine stopped.")
}
}
class Gearbox {
fun shiftGear(gear: Int) {
println("Gear shifted to $gear.")
}
}
class Brakes {
fun apply() {
println("Brakes applied.")
}
fun release() {
println("Brakes released.")
}
}
// 客户端代码
fun main() {
val car = CarFacade()
car.start()
car.stop()
}
- 封装复杂的子系统:外观模式可以将一个或多个复杂的子系统进行封装,使得客户端不需要了解子系统的内部实现,从而降低了客户端的复杂度。
- 提供简单的接口:外观模式提供了一个简单的接口,使得客户端可以更加方便地访问子系统,而不需要了解复杂的实现细节。
- 实现松散耦合:外观模式可以将客户端与子系统之间的依赖关系解耦,从而使得系统更加灵活和易于维护。
享元模式(Flyweight)
共享细粒度的对象,以节省内存。
interface Flyweight {
fun operation(state: String)
}
class ConcreteFlyweight : Flyweight {
override fun operation(state: String) {
println("ConcreteFlyweight: $state")
}
}
class FlyweightFactory {
private val flyweights = mutableMapOf<String, Flyweight>()
fun getFlyweight(key: String): Flyweight {
if (!flyweights.containsKey(key)) {
flyweights[key] = ConcreteFlyweight()
}
return flyweights[key]!!
}
}
- 共享对象:通过共享对象来减少系统中对象的数量,从而降低系统的内存占用和对象的创建时间。
- 工厂类维护:通过工厂类来维护共享的享元对象,并在需要时进行创建和获取。
- 对象复用:通过共享对象来实现对象的复用,避免了创建大量的对象,从而提高系统的性能和效率。
- 分离内部状态和外部状态:通过将对象的内部状态和外部状态分离,实现共享对象时只需共享内部状态,而外部状态可以根据具体的业务场景进行传递。
代理模式(Proxy)
通过代理控制对象的访问。
interface Subject {
fun request()
}
class RealSubject : Subject {
override fun request() {
println("RealSubject: Handling request.")
}
}
class Proxy(private val realSubject: RealSubject) : Subject {
override fun request() {
if (checkAccess()) {
realSubject.request()
logAccess()
}
}
private fun checkAccess(): Boolean {
println("Proxy: Checking access prior to firing a real request.")
return true
}
private fun logAccess() {
println("Proxy: Logging the time of request.")
}
}
fun main() {
val realSubject = RealSubject()
val proxy = Proxy(realSubject)
proxy.request()
}
- 控制对对象的访问:通过代理对象来控制对另一个对象的访问,从而达到控制对象的目的。
- 保护对象:通过代理对象来保护另一个对象,避免非法访问或恶意修改对象。
- 实现远程访问:通过代理对象来实现对象的远程访问,可以在不同的进程或机器之间进行通信,从而实现分布式系统。
- 实现延迟初始化:通过代理对象来实现对象的延迟初始化,可以根据需要来动态创建对象。
- 实现对象缓存:通过代理对象来实现对象的缓存,可以避免重复创建对象,提高系统的性能和效率。
职责链模式(Chain of Responsibility)
请求沿处理链传递,避免耦合。
interface Handler {
fun handleRequest(request: String): String?
fun setNextHandler(nextHandler: Handler)
}
class ConcreteHandlerA : Handler {
private var nextHandler: Handler? = null
override fun handleRequest(request: String): String? {
if (request == "A") {
return "ConcreteHandlerA handled the request"
} else {
return nextHandler?.handleRequest(request)
}
}
override fun setNextHandler(nextHandler: Handler) {
this.nextHandler = nextHandler
}
}
class ConcreteHandlerB : Handler {
private var nextHandler: Handler? = null
override fun handleRequest(request: String): String? {
if (request == "B") {
return "ConcreteHandlerB handled the request"
} else {
return nextHandler?.handleRequest(request)
}
}
override fun setNextHandler(nextHandler: Handler) {
this.nextHandler = nextHandler
}
}
fun main() {
val handlerA = ConcreteHandlerA()
val handlerB = ConcreteHandlerB()
handlerA.setNextHandler(handlerB)
println(handlerA.handleRequest("A"))
println(handlerA.handleRequest("B"))
println(handlerA.handleRequest("C"))
}
- 将请求的发送者和接收者解耦:职责链模式通过将请求的发送者和接收者解耦,避免了它们之间的直接依赖关系,从而提高了系统的灵活性和可扩展性。
- 将多个处理对象组成一条处理链:职责链模式将多个处理对象组成一条处理链,每个处理对象都有机会处理请求,如果自己无法处理请求,则将请求传递给下一个处理对象。
- 沿着链传递请求:职责链模式沿着链传递请求,直到有一个处理对象能够处理请求为止。处理对象的顺序可以根据实际情况进行动态配置,从而提高了系统的灵活性和可配置性。
- 动态增加或修改处理链:职责链模式允许动态增加或修改处理链,从而可以在运行时根据实际需要进行灵活的调整。
命令模式(Command)
封装请求对象,解耦发送和接收。
// 接收者类
class Receiver {
fun action() {
println("Receiver: executing an action.")
}
}
// 命令接口
interface Command {
fun execute()
}
// 具体命令类
class ConcreteCommand(private val receiver: Receiver) : Command {
override fun execute() {
receiver.action()
}
}
// 发起者类
class Invoker(private val command: Command) {
fun executeCommand() {
command.execute()
}
}
// 测试代码
fun main() {
val receiver = Receiver()
val command = ConcreteCommand(receiver)
val invoker = Invoker(command)
invoker.executeCommand()
}
- 将命令的发起者和执行者解耦:命令模式通过将命令的发起者和执行者解耦,使得它们不需要知道彼此的存在,从而提高了系统的灵活性和可扩展性。
- 将命令封装为对象:命令模式将命令封装为一个对象,使得可以将命令作为参数进行传递、保存和执行。
- 支持撤销操作:命令模式支持撤销操作,可以在执行命令前保存命令的状态,以便后续进行撤销操作。
- 支持宏命令:命令模式支持将多个命令组合成一个宏命令,从而可以一次执行多个命令。
- 可以在运行时动态添加或修改命令:命令模式可以在运行时动态添加或修改命令,从而可以根据实际需要进行灵活的调整。
解释器模式(Interpreter)
解释执行语言语句。
interface Expression {
fun interpret(): Int
}
class Number(private val n: Int) : Expression {
override fun interpret(): Int {
return n
}
}
class Add(private val left: Expression, private val right: Expression) : Expression {
override fun interpret(): Int {
return left.interpret() + right.interpret()
}
}
class Subtract(private val left: Expression, private val right: Expression) : Expression {
override fun interpret(): Int {
return left.interpret() - right.interpret()
}
}
- 定义语言的语法:将文法规则表示为一个类层次结构,由接口和具体类实现。
- 对表达式进行解释:根据语法规则解释表达式。
- 管理和执行解释器:将解释器和表达式绑定,执行解释器解释表达式。
迭代器模式(Iterator)
遍历集合元素。
interface Iterator<T> {
fun hasNext(): Boolean
fun next(): T
}
class MyList<T>(private val items: MutableList<T>) : Iterable<T> {
override fun iterator(): Iterator<T> {
return MyListIterator()
}
private inner class MyListIterator : Iterator<T> {
private var index = 0
override fun hasNext(): Boolean {
return index < items.size
}
override fun next(): T {
return items[index++]
}
}
}
- 提供一种统一的方式访问集合中的元素:迭代器模式为不同类型的集合提供了一致的遍历接口,使得遍历过程变得简单和统一。
- 隐藏集合内部的实现细节:迭代器模式将集合对象的遍历操作与其内部结构分离,避免了暴露集合的内部实现细节,从而更容易维护和扩展集合。
- 支持同时遍历多个集合对象:迭代器模式可以在同一时间遍历多个集合对象,从而方便地对集合进行组合、过滤、排序等操作。
- 提供不同类型的迭代器:迭代器模式提供了不同类型的迭代器,例如正向迭代器、反向迭代器、过滤迭代器、映射迭代器等,以满足不同的遍历需求。
中介者模式(Mediator)
解耦多个对象之间的交互关系。
interface Mediator {
fun notify(sender: Colleague, event: String)
}
abstract class Colleague(val mediator: Mediator) {
abstract fun notify(event: String)
}
class ConcreteColleague1(mediator: Mediator) : Colleague(mediator) {
override fun notify(event: String) {
println("ConcreteColleague1 gets notified: $event")
}
}
class ConcreteColleague2(mediator: Mediator) : Colleague(mediator) {
override fun notify(event: String) {
println("ConcreteColleague2 gets notified: $event")
}
}
class ConcreteMediator : Mediator {
private lateinit var colleague1: ConcreteColleague1
private lateinit var colleague2: ConcreteColleague2
fun setColleague1(colleague1: ConcreteColleague1) {
this.colleague1 = colleague1
}
fun setColleague2(colleague2: ConcreteColleague2) {
this.colleague2 = colleague2
}
override fun notify(sender: Colleague, event: String) {
when (sender) {
colleague1 -> colleague2.notify(event)
colleague2 -> colleague1.notify(event)
}
}
}
fun main() {
val mediator = ConcreteMediator()
val colleague1 = ConcreteColleague1(mediator)
val colleague2 = ConcreteColleague2(mediator)
mediator.setColleague1(colleague1)
mediator.setColleague2(colleague2)
colleague1.notify("Hello, Colleague2!")
}
- 降低类之间的耦合:中介者模式通过将对象之间的通信集中在中介者中,从而减少了对象之间的直接依赖关系,降低了类之间的耦合度。
- 简化对象之间的交互:中介者模式将对象之间的交互转移至中介者,使得对象之间的交互变得简单明了,易于理解和维护。
- 集中控制对象之间的交互:中介者模式通过集中控制对象之间的交互,可以更好地协调对象之间的行为,从而实现更复杂的系统功能。
- 提供灵活的扩展性:中介者模式使得系统易于扩展,因为它可以很容易地增加、替换或删除中介者对象和同事对象。
备忘录模式(Memento)
保存对象状态。
// 备忘录类,用于存储 Originator 对象的状态
data class Memento(val state: String)
// 原发器类,拥有一些需要保存状态的属性,并提供保存和恢复状态的方法
class Originator(var state: String) {
fun createMemento(): Memento {
return Memento(state)
}
fun restoreMemento(memento: Memento) {
state = memento.state
}
}
// 管理者类,负责保存和恢复 Originator 对象的状态
class Caretaker {
private var memento: Memento? = null
fun saveState(originator: Originator) {
memento = originator.createMemento()
}
fun restoreState(originator: Originator) {
memento?.let { originator.restoreMemento(it) }
}
}
- 记录对象内部状态
- 在不破坏封装性的前提下保存和恢复对象状态
观察者模式(Observer)
当对象状态发生变化时,通知它的依赖者。
interface Observer {
fun update(value: Int)
}
class Observable {
private val observers = mutableListOf<Observer>()
var value: Int = 0
set(value) {
field = value
notifyObservers()
}
fun registerObserver(observer: Observer) {
observers.add(observer)
}
fun unregisterObserver(observer: Observer) {
observers.remove(observer)
}
private fun notifyObservers() {
observers.forEach { it.update(value) }
}
}
class ConcreteObserver(private val name: String) : Observer {
override fun update(value: Int) {
println("$name: The value has been updated to $value")
}
}
fun main() {
val observable = Observable()
val observer1 = ConcreteObserver("Observer 1")
val observer2 = ConcreteObserver("Observer 2")
observable.registerObserver(observer1)
observable.registerObserver(observer2)
observable.value = 5
observable.unregisterObserver(observer2)
observable.value = 10
}
- 主题与观察者分离:主题对象(Subject)与观察者对象(Observer)分离,主题不需要知道观察者是谁,而且可以动态添加或删除观察者。
- 状态改变通知观察者:当主题状态改变时,会自动通知所有的观察者对象,让它们更新自己的状态。
状态模式(State)
根据对象的状态,改变对象的行为。
interface State {
fun doAction(context: Context)
}
class ConcreteStateA : State {
override fun doAction(context: Context) {
println("当前状态为 A")
context.state = ConcreteStateB()
}
}
class ConcreteStateB : State {
override fun doAction(context: Context) {
println("当前状态为 B")
context.state = ConcreteStateA()
}
}
class Context(var state: State) {
fun request() {
state.doAction(this)
}
}
fun main() {
val context = Context(ConcreteStateA())
context.request()
context.request()
context.request()
}
- 将对象的行为与其状态分离:状态模式的核心思想是将对象的行为与其状态分离,使得同一行为在不同的状态下有不同的实现。通过将状态封装成独立的类,可以将状态的变化独立于具体的对象,从而降低对象之间的耦合性。
- 在运行时改变对象的状态,从而改变其行为:状态模式允许在运行时改变对象的状态,从而改变其行为。在状态模式中,对象的行为取决于其当前的状态,因此可以通过改变状态来改变对象的行为。这种动态的行为改变使得状态模式在某些场景下比较适用,例如游戏中的角色状态等。
- 状态之间可以相互转换,而不会影响对象的行为:状态模式允许状态之间相互转换,而不会影响对象的行为。在状态模式中,状态的转换通常由对象的某些操作触发,例如用户输入等。状态转换的过程通常由具体的状态类来完成,这使得状态之间的转换更加灵活和可控。同时,由于状态转换并不影响对象的行为,因此状态模式可以很好地应对状态变化频繁的场景。
策略模式(Strategy)
定义一系列算法,封装每个算法,并使它们可以互换。
interface Strategy {
fun doOperation(num1: Int, num2: Int): Int
}
class AddStrategy : Strategy {
override fun doOperation(num1: Int, num2: Int): Int {
return num1 + num2
}
}
class SubtractStrategy : Strategy {
override fun doOperation(num1: Int, num2: Int): Int {
return num1 - num2
}
}
class Context(private val strategy: Strategy) {
fun executeStrategy(num1: Int, num2: Int): Int {
return strategy.doOperation(num1, num2)
}
}
fun main() {
val context = Context(AddStrategy())
println("10 + 5 = ${context.executeStrategy(10, 5)}")
context.strategy = SubtractStrategy()
println("10 - 5 = ${context.executeStrategy(10, 5)}")
}
- 将算法封装成独立的类:策略模式的核心思想是将算法封装成独立的类,使得算法可以独立于客户端而变化。每个算法都由一个独立的类来实现,这些类都实现了一个共同的接口,从而可以在客户端中互换使用。
- 在运行时选择算法,实现动态切换:策略模式允许在运行时选择算法,从而实现动态切换。客户端可以在运行时选择一个合适的算法,然后将其传递给上下文类,上下文类会使用该算法进行运算。这种动态选择算法的特性使得策略模式在某些场景下比较适用,例如对于需要在多个算法之间动态选择的场景。
- 避免使用大量的条件语句:策略模式避免了在客户端中使用大量的条件语句,从而使代码更加简洁和易于维护。客户端只需要知道有哪些算法可供选择,并且如何选择算法,而不需要关心算法的实现细节。这种解耦合的特性使得策略模式可以很好地应对算法变化频繁的场景。
模板方法模式(Template Method)
定义一个算法的骨架,具体实现由子类完成。
abstract class Game {
abstract fun initialize()
abstract fun startPlay()
abstract fun endPlay()
fun play() {
initialize()
startPlay()
endPlay()
}
}
class CricketGame : Game() {
override fun initialize() {
println("Cricket Game Initialized! Start playing.")
}
override fun startPlay() {
println("Cricket Game Started. Enjoy the game!")
}
override fun endPlay() {
println("Cricket Game Finished!")
}
}
fun main() {
val game = CricketGame()
game.play()
}
- 封装算法流程:模板方法模式封装了一个算法流程,该算法流程由多个步骤组成,其中有些步骤是固定的,而有些步骤可以根据具体情况变化。在模板方法模式中,固定的步骤通常是实现在抽象类中的,而可变的步骤通常是由具体子类实现的。
- 提高代码复用性:在模板方法模式中,算法流程被封装在抽象类中,具体实现交由子类完成。这种方式可以提高代码的复用性,因为多个子类可以共享相同的算法流程。
- 定义算法流程的骨架,具体实现交由子类完成:模板方法模式定义了一个算法流程的骨架,其中包含多个步骤,其中有些步骤是固定的,而有些步骤可以根据具体情况变化。在模板方法模式中,固定的步骤通常是实现在抽象类中的,而可变的步骤通常是由具体子类实现的。这种方式可以很好地分离出算法流程的通用部分和具体实现部分,从而提高代码的灵活性和可维护性。
访问者模式(Visitor)
封装对数据结构的操作。
interface ComputerPartVisitor {
fun visit(computer: Computer)
fun visit(keyboard: Keyboard)
fun visit(mouse: Mouse)
fun visit(printer: Printer)
}
interface ComputerPart {
fun accept(visitor: ComputerPartVisitor)
}
class Keyboard : ComputerPart {
override fun accept(visitor: ComputerPartVisitor) {
visitor.visit(this)
}
}
class Mouse : ComputerPart {
override fun accept(visitor: ComputerPartVisitor) {
visitor.visit(this)
}
}
class Printer : ComputerPart {
override fun accept(visitor: ComputerPartVisitor) {
visitor.visit(this)
}
}
class Computer : ComputerPart {
private val parts: Array<ComputerPart> = arrayOf(
Keyboard(),
Mouse(),
Printer()
)
override fun accept(visitor: ComputerPartVisitor) {
for (part in parts) {
part.accept(visitor)
}
visitor.visit(this)
}
}
class ComputerPartDisplayVisitor : ComputerPartVisitor {
override fun visit(computer: Computer) {
println("Displaying Computer.")
}
override fun visit(keyboard: Keyboard) {
println("Displaying Keyboard.")
}
override fun visit(mouse: Mouse) {
println("Displaying Mouse.")
}
override fun visit(printer: Printer) {
println("Displaying Printer.")
}
}
fun main() {
val computer: ComputerPart = Computer()
computer.accept(ComputerPartDisplayVisitor())
}
- 将数据结构与数据操作分离:在访问者模式中,数据结构与数据操作被分离开来。数据结构是指一组对象的集合,而数据操作是指对这些对象的某些操作。通过将数据操作封装在访问者中,可以将数据结构与数据操作分离开来,从而更好地管理和组织代码。
- 增加新的操作不需要修改已有的类:在访问者模式中,数据结构中的每个对象都实现了一个accept方法,用于接受访问者的访问。当需要增加一种新的操作时,只需要创建一个新的访问者类,并实现该操作即可,而不需要修改已有的数据结构类。
- 将同一操作应用于不同的对象,实现操作的重用:在访问者模式中,所有的访问者都实现了相同的访问方法,这意味着可以将同一操作应用于不同的对象,实现操作的重用。例如,可以创建一个ComputerPartDisplayVisitor访问者,用于显示计算机部件的信息,然后将该访问者应用于不同的计算机部件对象,从而实现操作的重用。