代码复用:避免重复,增强可维护性。
提高灵活性:调整对象创建方式,改变程序行为。
解耦:清晰化依赖,便于代码改动。
控制对象创建:精确管理对象的创建和初始化。
只针对一个抽象产品类进行创建。每个具体工厂类只能创建一个具体产品类的实例。
下面是一个与Android相关的工厂方法模式的例子,这个例子中我们将创建一个简单的ViewFactory来创建不同类型的View对象。
1、创建一个接口来表示视图(View)
interface View {
fun onMeasure()
fun onLayout()
fun onDraw()
}
2、实现了View接口的具体视图类
class TextView: View {
override fun onMeasure() { print("TextView onMeasure") }
override fun onLayout() { print("TextView onLayout") }
override fun onDraw() { print("TextView onDraw") }
}
class Button: View {
override fun onMeasure() { print("Button onMeasure") }
override fun onLayout() { print("Button onLayout") }
override fun onDraw() { print("Button onDraw") }
}
3、创建ViewFactory
class ViewFactory {
fun createView(viewType: String): View? {
return when (viewType) {
"TEXT_VIEW" -> TextView()
"BUTTON" -> Button()
else -> null
}
}
}
4、客户端 通过ViewFactory来创建对应的View
fun main() {
val viewFactory = ViewFactory()
val textView = viewFactory.createView("TEXT_VIEW")
textView?.onMeasure()
textView?.onLayout()
textView?.onDraw()
val button = viewFactory.createView("BUTTON")
textView?.onMeasure()
textView?.onLayout()
textView?.onDraw()
}
在这个示例中,ViewFactory 工厂类通过其 createView 方法根据传入的类型字符串创建并返回相应的视图对象。客户端代码调用 createView 来获取视图对象,并调用其 draw 方法进行绘制。这种方式实现了客户端代码与具体视图实现的解耦,使得添加新的视图类型时只需更新工厂类,无需修改客户端代码,提高了代码的可扩展性和可维护性。
针对多个产品族进行创建,即每个工厂可以创建多种不同类型的产品。每个具体工厂类可以创建多个具体产品类的实例。
以下我会以汽车来举例
1、定义接口或者抽象类
// 汽车接口
interface Car {
fun startEngine()
fun drive()
}
// 电池接口
interface Battery {
fun charge()
fun powerCar()
}
2、实现具体类
// 电动汽车
class ElectricCar : Car {
override fun startEngine() {
print("Starting Electric Engine")
}
override fun drive() {
print("Driving Electric Car")
}
}
// 电动汽车电池
class ElectricBattery : Battery {
override fun charge() {
print("Charging Electric Battery")
}
override fun powerCar() {
print("Powering Electric Car with Electric Battery")
}
}
// 汽油车
class GasolineCar : Car {
override fun startEngine() {
print("Starting Gasoline Engine")
}
override fun drive() {
print("Driving Gasoline Car")
}
}
// 汽油车电池
class GasolineBattery : Battery {
override fun charge() {
print("Charging Gasoline Battery")
}
override fun powerCar() {
print("Powering Gasoline Car with Gasoline Battery")
}
}
3、实现工厂具体类
// 电动汽车工厂
class ElectricCarFactory : CarFactory {
override fun createCar(): Car {
return ElectricCar()
}
override fun createBattery(): Battery {
return ElectricBattery()
}
}
// 汽油车工厂
class GasolineCarFactory : CarFactory {
override fun createCar(): Car {
return GasolineCar()
}
override fun createBattery(): Battery {
return GasolineBattery()
}
}
4、客户端代码中使用抽象工厂来创建产品族
fun main() {
// 创建电动汽车工厂
val electricCarFactory = ElectricCarFactory()
val electricCar = electricCarFactory.createCar()
val electricBattery = electricCarFactory.createBattery()
electricCar.startEngine()
electricCar.drive()
electricBattery.charge()
electricBattery.powerCar()
// 创建汽油车工厂
val gasolineCarFactory = GasolineCarFactory()
val gasolineCar = gasolineCarFactory.createCar()
val gasolineBattery = gasolineCarFactory.createBattery()
gasolineCar.startEngine()
gasolineCar.drive()
gasolineBattery.charge()
gasolineBattery.powerCar()
}
在以上这个例子中,CarFactory 是一个工厂接口,负责创建 Car 和 Battery 对象。ElectricCarFactory 和 GasolineCarFactory 是具体工厂类,分别创建电动汽车和汽油车及其对应的电池。客户端代码通过这些工厂创建对象并调用其方法。这种设计遵循开闭原则,易于扩展和维护,因为添加新类型时只需创建新的产品和工厂类,无需修改客户端代码。
单例模式的五种实现方式
用于确保一个类只有一个实例,并提供一个全局访问点来访问该实例,常用于控制资源的使用、节约资源、数据共享和简化代码。
//双重校验锁式
class SingletonTest private constructor() {
companion object {
val instance: SingletonTest by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
SingletonTest()
}
}
}
结合 lazy 委托和 by 关键字,实现一个线程安全的延迟初始化单例。
让专业的建造者来负责创建复杂的产品,客户只需要提供需求,就能得到想要的产品,而且这个过程是灵活和可扩展的。
// 产品类
class AndroidDevice {
var brand: String? = null
var model: String? = null
var osVersion: String? = null
var screenSize: String? = null
}
// 建造者类
class AndroidDeviceBuilder {
private var brand: String? = null
private var model: String? = null
private var osVersion: String? = null
private var screenSize: String? = null
fun setBrand(brand: String): AndroidDeviceBuilder {
this.brand = brand
return this
}
fun setModel(model: String): AndroidDeviceBuilder {
this.model = model
return this
}
fun setOsVersion(osVersion: String): AndroidDeviceBuilder {
this.osVersion = osVersion
return this
}
fun setScreenSize(screenSize: String): AndroidDeviceBuilder {
this.screenSize = screenSize
return this
}
fun build(): AndroidDevice {
val device = AndroidDevice()
device.brand = this.brand
device.model = this.model
device.osVersion = this.osVersion
device.screenSize = this.screenSize
return device
}
}
// 使用
fun main() {
val device = AndroidDeviceBuilder()
.setBrand("XiaoMi")
.setModel("S100")
.setOsVersion("Android 122")
.setScreenSize("6.7 inches")
.build()
}
上面我们定义了AndroidDevice类来表示安卓设备,并使用AndroidDeviceBuilder类作为建造者来构建它。通过链式调用的方式,我们可以轻松设置设备的属性并最终调用build方法来获取一个完整的设备实例。这种设计让对象的创建更加简洁和灵活。
简单来说就是一种创建新对象的方法,它不是通过常规的new关键字来创建,而是通过复制(克隆)一个已有的对象来得到新的对象。
原型模式主要通过 深拷贝或浅拷贝来实现
浅拷贝:
// 定义一个数据类作为原型
data class Person(val name: String, val age: Int)
// 使用copy()方法创建新实例,相当于克隆
fun main() {
// 创建一个原型对象
val person1 = Person("Alice", 30)
// 克隆原型对象
val person2 = person1.copy(name = "Bob") // 只改变了name属性,age保持不变
// 输出结果
println("Person 1: ${person1.name}, ${person1.age}")
println("Person 2: ${person2.name}, ${person2.age}")
}
Person类没有包含任何引用类型的字段,所以这里的copy()方法并没有执行真正的深拷贝。
如果Person类包含引用类型的字段,且这些字段在copy()方法中只是被浅拷贝,那么Person类的copy()方法将执行浅拷贝。
深拷贝:
class ComplexObject(val data: MutableList<String>) {
// 手动实现的copy函数,执行深拷贝
fun copy(): ComplexObject{
return ComplexObject(data.toMutableList()) // 创建新的列表副本
}
}
fun main() {
val original = ComplexObject(mutableList.of("a", "b", "c"))
val cloned = original.copy() // 执行深拷贝
cloned.data.add("d") // 修改克隆对象的数据
println("Original: ${original.data}") // 输出原始对象的数据
println("Cloned: ${cloned.data}") // 输出克隆对象的数据
}
data是一个引用类型的字段。在copy()方法中,我们通过调用toMutableList()创建了data列表的一个新副本,这意味着新对象和原始对象的data字段将引用两个不同的列表。因此,对cloned对象的data列表所做的任何修改都不会影响到original对象的data列表
简而言之,原型模式允许你通过复制现有对象来快速创建新对象,从而提高了性能和简化了对象创建过程。
降低耦合:降低类之间的依赖,提高系统的灵活性和可维护性。
方便扩展:使系统容易添加新功能,而不影响现有功能。
简化设计:通过分解复杂问题为更小的部分,降低系统复杂性。
适配器模式就像是找了一个翻译,帮助两个语言不通的人顺畅交流。在编程中,当两个系统或类无法直接沟通时,适配器模式可以作为一个“翻译”,让它们能够相互理解和协作,而不需要改变它们本身的代码。这样一来,既保护了原有系统的稳定性,又提高了代码的复用性和系统的灵活性。
// 旧版音频播放器接口
interface OldAudioPlayer {
fun playOldAudio(fileName: String)
}
// 新版音频播放器接口
interface NewAudioPlayer {
fun playAudio(fileName: String)
}
// 实现新版音频播放器接口的类
class ModernAudioPlayer : NewAudioPlayer {
override fun playAudio(fileName: String) {
println("Playing audio from $fileName using ModernAudioPlayer")
}
}
// 适配器类,实现旧版音频播放器接口,但内部使用新版音频播放器
class OldAudioPlayerAdapter(private val modernPlayer: NewAudioPlayer) : OldAudioPlayer {
override fun playOldAudio(fileName: String) {
modernPlayer.playAudio(fileName)
}
}
// 使用适配器的示例
fun main() {
// 创建新版音频播放器实例
val modernPlayer = ModernAudioPlayer()
// 创建适配器,将新版音频播放器适配为旧版音频播放器
val oldPlayerAdapter = OldAudioPlayerAdapter(modernPlayer)
// 使用旧版接口方法调用新版音频播放器
oldPlayerAdapter.playOldAudio("example.mp3")
// 输出: Playing audio from example.mp3 using ModernAudioPlayer
}
在这个例子中,OldAudioPlayerAdapter就是一个适配器,它使得实现了NewAudioPlayer接口的ModernAudioPlayer能够在需要OldAudioPlayer接口的地方被使用。这样,我们就可以在不修改旧版音频播放方法playOldAudio的情况下,利用新版的ModernAudioPlayer来播放音频。
在实际应用中经常用来解决接口不兼容的问题,比如将旧的系统接口适配到新的系统接口,或者在不同的系统或库之间进行桥接。它提供了一种灵活的方式来复用已有的代码,而不需要对原有的代码结构进行大规模的修改。
例如:Android的RecyclerView、GridView等组件都用到设配器模式
通过将抽象部分与实现部分分离,使得它们可以独立变化。
让我们用一个日常生活中的例子来解释桥接模式。假设我们要设计一个电视遥控器,这个遥控器可以控制不同品牌和型号的电视。
1、定义TV接口
// 电视操作接口
interface TV {
fun turnOn()
fun turnOff()
fun changeChannel(channel: Int)
}
2、TV接口具体实现
// 小米电视实现
class XiaomiTV : TV {
override fun turnOn() {
println("Xiaomi TV is turned on.")
}
override fun turnOff() {
println("Xiaomi TV is turned off.")
}
override fun changeChannel(channel: Int) {
println("Xiaomi TV channel changed to $channel.")
}
}
// LG电视实现
class LGTV : TV {
override fun turnOn() {
println("LG TV is turned on.")
}
override fun turnOff() {
println("LG TV is turned off.")
}
override fun changeChannel(channel: Int) {
println("LG TV channel changed to $channel.")
}
}
3、定义一个遥控器抽象类,它包含一个对电视接口的引用
// 遥控器抽象类
abstract class RemoteControl(protected val tv: TV) {
abstract fun pressButton(button: String)
fun operateTV(): String {
return "Pressing button on the remote control will operate the TV."
}
}
4、不同的遥控器按钮提供具体的实现
// 普通遥控器实现
class RegularRemote(tv: TV) : RemoteControl(tv) {
override fun pressButton(button: String) {
when (button) {
"power" -> tv.turnOn()
"off" -> tv.turnOff()
else -> tv.changeChannel(button.toInt())
}
}
}
// 智能遥控器实现
class SmartRemote(tv: TV) : RemoteControl(tv) {
override fun pressButton(button: String) {
when (button) {
"power" -> tv.turnOn()
"off" -> tv.turnOff()
else -> {
tv.changeChannel(button.toInt())
println("Smart Remote also displays additional info about the channel.")
}
}
}
}
5、使用
fun main() {
val XiaomiTV = XiaomiTV()
val lgTV = LGTV()
val regularRemoteForXiaomi = RegularRemote(XiaomiTV)
val smartRemoteForLG = SmartRemote(lgTV)
regularRemoteForXiaomi.pressButton("power") // 输出:Xiaomi TV is turned on.
smartRemoteForLG.pressButton("5") // 输出:LG TV channel changed to 5. Smart Remote also displays additional info about the channel.
}
在上述例子中,TV 接口代表了电视的不同品牌和型号的行为,而 RemoteControl 抽象类代表了遥控器。RemoteControl 通过持有 TV 的引用,将遥控器和电视的行为解耦。
这样,我们可以为不同的电视品牌和型号创建不同的实现,而不需要修改遥控器的代码。
同样,我们也可以为不同的遥控器类型(如普通遥控器和智能遥控器)创建不同的实现,而不需要修改电视的代码。这就是桥接模式的核心思想:将抽象部分与实现部分分离,使它们可以独立地变化。
将对象组合成树形结构以表示部分-整体的层次结构的设计模式。它使得用户对单个对象和组合对象的适用具有一致性,即客户端代码可以像处理简单元素一样来处理复杂元素,从而使得客户端代码与复杂元素的内部结构解耦。
像我们生活中的一些组合玩具,比如一个大的积木套装,里面包含了各种小积木。这些小积木可以单独使用,也可以组合在一起创建出更大的结构。这种设计使得我们可以灵活地创建出各种不同的玩具模型。
1、定义一个积木接口
interface Block {
fun build()
fun add(block: Block)
}
2、创建具体类(积木类)
class SquareBlock : Block {
override fun build() {
println("Building a square block.")
}
override fun add(block: Block) {
// 方块积木通常不会添加其他积木,所以这里什么也不做
}
}
class RectangleBlock : Block {
override fun build() {
println("Building a Rectangle block.")
}
override fun add(block: Block) {
// 长方形积木通常不会添加其他积木,所以这里什么也不做
}
}
3、创建一个复合类
class CompositeBlock : Block {
private val blocks: MutableList<Block> = mutableList.of()
override fun build() {
println("Building a composite block with multiple parts.")
blocks.forEach { it.build() }
}
override fun add(block: Block) {
blocks.add(block)
}
}
5、用这些具体类(积木类)来构建一个复合结构
fun main() {
// 创建一个复合积木
val compositeBlock = CompositeBlock()
// 创建一些方块积木
val rectangleBlock = RectangleBlock()
val squareBlock = SquareBlock()
// 将方块积木添加到复合积木中
compositeBlock.add(rectangleBlock)
compositeBlock.add(squareBlock2)
// 构建复合积木
compositeBlock.build()
// 输出:
// Building a composite block with multiple parts.
// Building a rectangle block.
// Building a square block.
}
在这个例子中,Block接口定义了积木的通用行为,而SquareBlock、RectangleBlock 和CompositeBlock类则分别实现了这个接口。
SquareBlock和RectangleBlock 是一个不可再组合的积木,而CompositeBlock则可以包含其他积木,并允许我们向其添加更多的积木。
通过add方法,我们可以将SquareBlock、RectangleBlock 实例添加到CompositeBlock中,形成一个更大的结构。
像Android的RecyclerView也是用的组合模式,将多个View组合到一起。
1、允许我们在不改变对象结构的情况下动态地扩展对象的功能,从而提高了代码的复用性和可维护性。
2、提供了一种灵活且可扩展的方式来动态地添加对象的功能。
例子如下:
// 定义一个饮品接口
interface Beverage {
fun description(): String
fun cost(): Double
}
// 创建一个具体的饮品类:咖啡
class Coffee : Beverage {
override fun description(): String = "Coffee"
override fun cost(): Double = 1.99
}
// 创建一个装饰者抽象类
abstract class BeverageDecorator(protected val beverage: Beverage) : Beverage {
override fun description(): String = "${beverage.description()} with Decoration"
override fun cost(): Double = beverage.cost() + 0.5 // 假设每个装饰增加0.5元的成本
}
// 创建具体的装饰者类
class MilkDecorator(beverage: Beverage) : BeverageDecorator(beverage) {
override fun description(): String = "${beverage.description()} and Milk"
}
class SugarDecorator(beverage: Beverage) : BeverageDecorator(beverage) {
override fun description(): String = "${beverage.description()} and Sugar"
}
// 在客户端代码中使用这些类和接口
fun main() {
val coffee: Beverage = Coffee()
val coffeeWithMilk: Beverage = MilkDecorator(coffee)
val coffeeWithSugar: Beverage = SugarDecorator(coffeeWithMilk)
println("Regular coffee: ${coffee.description()}, Cost: ${coffee.cost()}")
println("Coffee with milk:${coffeeWithMilk.description()},Cost: ${coffeeWithMilk.cost()}")
println("Coffee with milk and sugar: ${coffeeWithSugar.description()}, Cost:${coffeeWithSugar.cost()}")
}
//Regular coffee: Coffee, Cost: $1.99
//Coffee with milk: Coffee and Milk, Cost: $2.49
//Coffee with milk and sugar: Coffee and Milk and Sugar, Cost: $2.99
在这个例子中,Beverage 是接口,Coffee 是实现了 Beverage 接口的具体饮品。BeverageDecorator 是一个抽象装饰者类,它持有一个 Beverage 对象,并提供了一个默认的实现来添加装饰和增加成本。
MilkDecorator 和 SugarDecorator 是具体的装饰者类,它们分别给饮品添加了牛奶和糖,并覆盖了 description 方法来提供不同的描述。
外观模式和装饰者模式虽然文字意义差不多,但是还是有比较大的去别的。
装饰者模式是在不改变原有对象的基础上,给它增加新的功能或属性;
而外观模式则是提供一个简单、统一的入口,来整合和管理一群复杂的子系统或功能。
假设我们有一个复杂的子系统,它包含多个功能,每个功能都是一个类。我们可以使用外观模式来提供一个简单的接口来访问这些功能。
1、定义子系统的各个功能类
class SubsystemA {
fun operationA() {
println("Subsystem A operation")
}
}
class SubsystemB {
fun operationB() {
println("Subsystem B operation")
}
}
class SubsystemC {
fun operationC() {
println("Subsystem C operation")
}
}
2、定义一个外观类,它将这些子系统的功能整合在一起,并提供一个统一的接口
class Facade {
private val subsystemA = SubsystemA()
private val subsystemB = SubsystemB()
private val subsystemC = SubsystemC()
fun performOperations() {
subsystemA.operationA()
subsystemB.operationB()
subsystemC.operationC()
}
}
3、使用
fun main() {
val facade = Facade()
facade.performOperations()
}
通过外观模式,我们提供了一个简单的接口Facade来访问子系统的功能。客户端只需要与Facade交互,而无需了解子系统的具体实现细节,从而简化了客户端的使用。
通过共享不变的部分(内部状态)来减少大量相似对象造成的内存消耗,从而提高性能。
// 抽象享元
interface Shape {
fun draw(color: String)
}
// 具体享元
class Circle : Shape {
override fun draw(color: String) {
println("Drawing a circle in $color")
}
}
// 享元工厂
object ShapeFactory {
private val shapes = mutableMapOf<String, Shape>()
fun getShape(shapeType: String): Shape {
return shapes.getOrPut(shapeType) {
when (shapeType) {
"circle" -> Circle()
else -> throw IllegalArgumentException("Unknown shape type: $shapeType")
}
}
}
fun clear() {
shapes.clear()
}
}
// 使用示例
fun main() {
val circle1 = ShapeFactory.getShape("circle")
val circle2 = ShapeFactory.getShape("circle")
// 这两个circle实际上是同一个对象
println(circle1 === circle2) // 输出: true
circle1.draw("red")
circle2.draw("blue")
}
上述示例中,ShapeFactory 是享元工厂,它确保对于给定的形状类型,只创建一个Shape对象。Circle 是具体享元,它实现了Shape接口。
在Android开发中,享元模式可能用于:
1、图形渲染优化:在需要绘制大量相似图形时,通过享元模式减少图形对象的创建。
2、列表/RecyclerView优化:在显示大量数据时,通过享元模式重用和回收视图对象。
3、缓存和内存管理:通过享元模式管理资源,减少内存占用。
为客户端与实际对象之间提供一个中介,客户端并不直接调用实际的对象,而是通过调用代理对象来间接地调用实际的对象
下面举一个静态代理的例子
//定义一个接口
interface ImageService {
fun displayImage(): String
}
//接口实现类
class RealImageService : ImageService {
override fun displayImage(): String {
// 这里是加载和显示图片的实际逻辑
return "Displaying real image"
}
}
//创建一个代理类,它也实现了ImageService接口,并持有一个RealImageService的实例,以便代理其方法调用
class ProxyImageService(private val realImageService: RealImageService) : ImageService {
override fun displayImage(): String {
// 在调用实际方法之前或之后,可以添加额外的逻辑
println("Before displaying the image...")
// 调用实际的服务对象的方法
val result = realImageService.displayImage()
// 在调用实际方法之后,可以添加额外的逻辑
println("After displaying the image...")
return result
}
}
//创建一个ProxyImageService对象,并通过它来调用displayImage方法,代理类会在调用实际方法之前和之后执行额外的逻辑
fun main() {
// 创建实际的服务对象
val realService = RealImageService()
// 创建代理对象
val proxyService = ProxyImageService(realService)
// 通过代理对象调用方法
val imageDisplayResult = proxyService.displayImage()
// 输出结果
println(imageDisplayResult)
}
//输出
//Before displaying the image...
//Displaying real image
//After displaying the image...
促进协作:使对象能相互协作完成任务。
明确职责:清晰划分类与对象的责任。
增强扩展性:易于添加新功能,修改系统行为。
允许子类在不改变算法结构的情况下重新定义算法的某些步骤。这种模式将算法的不变部分封装在父类中,而将可变部分留给子类来实现,从而提高了代码的复用性和可扩展性
例子如下:
1、定义一个抽象的“模板方法”类,它包含了一个执行算法的框架和一些抽象方法
abstract class AbstractTemplate {
// 模板方法
fun executeAlgorithm() {
prepare()
process()
finalize()
}
// 抽象方法,由子类实现
abstract fun prepare()
abstract fun process()
abstract fun finalize()
}
2、创建一个或多个具体子类,它们提供了模板方法中抽象方法的具体实现
class ConcreteTemplateA : AbstractTemplate() {
override fun prepare() {
println("ConcreteTemplateA: 准备步骤")
}
override fun process() {
println("ConcreteTemplateA: 处理步骤")
}
override fun finalize() {
println("ConcreteTemplateA: 完成步骤")
}
}
class ConcreteTemplateB : AbstractTemplate() {
override fun prepare() {
println("ConcreteTemplateB: 准备步骤")
}
override fun process() {
println("ConcreteTemplateB: 处理步骤")
}
override fun finalize() {
println("ConcreteTemplateB: 完成步骤")
}
}
3、使用方法
fun main() {
val templateA = ConcreteTemplateA()
templateA.executeAlgorithm() // 输出 ConcreteTemplateA 的步骤
val templateB = ConcreteTemplateB()
templateB.executeAlgorithm() // 输出 ConcreteTemplateB 的步骤
}
//输出
//ConcreteTemplateA: 准备步骤
//ConcreteTemplateA: 处理步骤
//ConcreteTemplateA: 完成步骤
//ConcreteTemplateB: 准备步骤
//ConcreteTemplateB: 处理步骤
//ConcreteTemplateB: 完成步骤
这个模式在Android开发中特别有用,例如在创建网络请求、数据处理或视图渲染等场景中,你可能想要保持某些步骤(如初始化、执行和清理)的顺序不变,但希望在不同的情境下定制这些步骤的具体行为。
策略模式就像有多个工具箱,每个工具箱里都有不同的工具(算法或行为)。当我们需要完成某个任务时,可以选择合适的工具箱(策略),从中挑选适合的工具来用。这样,我们就可以根据任务的需要灵活地更换工具箱和工具,而不需要每次都重新发明轮子。这样做不仅让我们的工作更高效,也让代码更容易理解和维护。
例子如下
假设我们有一个支付系统,它支持多种支付方式,如支付宝、微信支付和银联支付。我们可以使用策略模式来定义这些不同的支付方式。
1、定义一个支付策略接口
interface PaymentStrategy {
fun pay(amount: Double): String
}
2、我们为每种支付方式实现这个接口
class AlipayStrategy : PaymentStrategy {
override fun pay(amount: Double): String {
return "使用支付宝支付了 $amount 元"
}
}
class WechatPayStrategy : PaymentStrategy {
override fun pay(amount: Double): String {
return "使用微信支付了 $amount 元"
}
}
class UnionPayStrategy : PaymentStrategy {
override fun pay(amount: Double): String {
return "使用银联支付了 $amount 元"
}
}
3、定义一个上下文类,它使用支付策略
class PaymentContext(private val strategy: PaymentStrategy) {
fun executePayment(amount: Double) {
println(strategy.pay(amount))
}
}
4、根据需要选择不同的支付策略
fun main() {
val alipay = AlipayStrategy()
val wechatPay = WechatPayStrategy()
val unionPay = UnionPayStrategy()
val paymentContextAlipay = PaymentContext(alipay)
paymentContextAlipay.executePayment(100.0) // 输出:使用支付宝支付了 100.0 元
val paymentContextWechatPay = PaymentContext(wechatPay)
paymentContextWechatPay.executePayment(200.0) // 输出:使用微信支付了 200.0 元
val paymentContextUnionPay = PaymentContext(unionPay)
paymentContextUnionPay.executePayment(300.0) // 输出:使用银联支付了 300.0 元
}
在这个例子中,PaymentStrategy 是策略接口,AlipayStrategy、WechatPayStrategy 和 UnionPayStrategy 是具体的策略实现。
PaymentContext 是上下文类,它接受一个策略对象,并调用该策略对象的方法来完成支付。
在主函数中,我们演示了如何使用不同的策略来执行支付操作。
也被称为状态机模式(State Machine Pattern)可以被用来管理复杂对象的生命周期或行为,这些对象的状态可能会随着应用程序的不同事件而改变
状态机模式的示例:
// 定义状态接口
interface State {
fun handle(context: Context)
}
// 定义具体的状态类
enum class Context {
STATE_A, STATE_B, STATE_C
}
class StateA : State {
override fun handle(context: Context) {
when (context) {
Context.STATE_A -> println("Handling State A")
Context.STATE_B -> {
println("Transitioning to State B")
// 执行状态转换逻辑
}
else -> println("Invalid transition from State A")
}
}
}
class StateB : State {
override fun handle(context: Context) {
when (context) {
Context.STATE_B -> println("Handling State B")
Context.STATE_C -> {
println("Transitioning to State C")
// 执行状态转换逻辑
}
else -> println("Invalid transition from State B")
}
}
}
// 定义环境类
class StateContext(private val initialState: State) {
private var currentState: State = initialState
fun setState(state: State) {
currentState = state
}
fun handle() {
currentState.handle(this.currentState)
}
fun transitionTo(newState: State) {
// 逻辑可以更复杂,例如根据当前状态和新状态来决定是否允许转换
setState(newState)
}
}
// 使用示例
fun main() {
val context = StateContext(StateA())
context.handle() // Handling State A
context.transitionTo(StateB())
context.handle() // Handling State B
context.transitionTo(StateA())
context.handle() // Handling State A
}
在这个例子中,我们定义了三个状态:StateA、StateB 和 StateC,它们实现了 State 接口。StateContext 类是环境类,它维护了一个当前状态(currentState),并提供了处理状态的方法(handle)和状态转换的方法(transitionTo)。
在真实的应用中,状态机可能会更加复杂,例如:
状态转换可能依赖于多种条件。
每个状态可能有多个行为。
状态转换可能涉及多个对象的协作。
蓝牙的状态机模式就是比较复杂的情况,有兴趣的可以去看看Android的framework蓝牙的相关代码
它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象状态改变时,会通知所有依赖于它的观察者对象,使它们能够自动更新自己。例如:Android的LiveData的实现方式就是livedata
我们自定义一个观察者模式的例子:
interface Observer<in T> {
fun update(data: T)
}
class Subject<T> {
private val observers = mutableList<Observer<T>>()
fun attach(observer: Observer<T>) {
observers.add(observer)
}
fun detach(observer: Observer<T>) {
observers.remove(observer)
}
fun notify(data: T) {
for (observer in observers) {
observer.update(data)
}
}
}
// 使用
class MyObserver : Observer<String> {
override fun update(data: String) {
println("Received data: $data")
}
}
fun main() {
val subject = Subject<String>()
val observer = MyObserver()
subject.attach(observer)
subject.notify("Hello, World!") // 输出 "Received data: Hello, World!"
subject.detach(observer)
}
在这个例子中,Subject 类负责管理观察者列表,并提供方法来添加、移除和通知观察者。
Observer 接口定义了观察者的行为。
总的来说,在 Android 开发中,观察者模式提供了多种实现方式,允许开发者根据具体需求选择最合适的方法来实现数据驱动的用户界面和组件间通信。
又叫快照模式(Snapshot Pattern)或Token模式。备忘录模式的主要目的是在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。
一个简单的Kotlin示例,展示了如何在Android应用中实现备忘录模式:
1、定义一个Memento类,用于存储备忘录数据
data class Memento(val content: String, val timestamp: Long)
2、创建一个Originator类,它包含了需要保存和恢复的内部状态,以及创建备忘录和从备忘录恢复状态的方法
class Originator {
private var content: String = ""
private var timestamp: Long = 0
// 获取当前状态并创建备忘录
fun createMemento(): Memento {
return Memento(content, System.currentTimeMillis())
}
// 设置状态
fun setState(content: String) {
this.content = content
this.timestamp = System.currentTimeMillis()
}
// 从备忘录恢复状态
fun restoreMemento(memento: Memento) {
this.content = memento.content
this.timestamp = memento.timestamp
}
// 显示当前状态
fun displayState() {
println("Content: $content, Timestamp: $timestamp")
}
}
3、使用示例
class MainActivity : AppCompatActivity() {
private lateinit var originator: Originator
private lateinit var caretaker: Caretaker
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
originator = Originator()
caretaker = Caretaker()
// 设置初始状态
originator.setState("Initial content")
originator.displayState()
// 创建并保存备忘录
val memento = originator.createMemento()
caretaker.saveMemento(memento)
// 修改状态
originator.setState("Modified content")
originator.displayState()
// 从备忘录恢复状态
originator.restoreMemento(caretaker.getMemento()!!)
originator.displayState()
}
}
在这个例子中,我们创建了一个Originator对象,并设置了它的初始状态。然后,我们创建了一个备忘录并将其保存到Caretaker对象中。之后,我们修改了Originator的状态,并通过Caretaker恢复到了之前保存的状态。
这只是一个简单的示例,用于说明如何在Android中实现备忘录模式。在实际应用中,可能需要根据你的具体需求来调整这些类的设计和实现。
提供了一种方法来顺序访问一个聚合对象(如数组、列表或其他数据结构)中的各个元素,而又不暴露该对象的内部表示。
1、以下是一个简单的实现,其中定义了一个聚合(MyCollection)和一个迭代器(MyIterator)
// 迭代器接口
interface Iterator<out T> {
fun hasNext(): Boolean
fun next(): T
}
// 聚合接口
interface Collection<out T> : Iterable<T> {
fun createIterator(): Iterator<T>
}
// 具体聚合实现
class MyCollection<T>(private val elements: List<T>) : Collection<T> {
override fun createIterator(): Iterator<T> {
return MyIterator(elements)
}
override fun iterator(): Iterator<T> {
return createIterator()
}
}
// 具体迭代器实现
class MyIterator<T>(private val elements: List<T>, private var currentIndex: Int = 0) : Iterator<T> {
override fun hasNext(): Boolean {
return currentIndex < elements.size
}
override fun next(): T {
if (!hasNext()) {
throw NoSuchElementException("No more elements")
}
return elements[currentIndex++]
}
}
// 使用示例
fun main() {
val myCollection = MyCollection(listOf(1, 2, 3, 4, 5))
val iterator = myCollection.createIterator()
while (iterator.hasNext()) {
println(iterator.next())
}
}
在上面的例子中,MyCollection 是一个具体的聚合类,它持有一个元素列表,并实现了 Collection 接口,以提供 createIterator 方法来创建迭代器。MyIterator 是一个实现了 Iterator 接口的具体迭代器类,它遍历 MyCollection 中的元素。
表示一个作用于某对象结构中的各元素的操作,可以在不改变各元素类的前提下定义作用于这些元素的新操作
我们拿大家熟悉的一些生活中的例子举例
在医院的门诊部场景中,我们可以将门诊部看作是一个抽象元素(Element),而具体的科室(如内科、外科、儿科等)可以看作是具体元素(ConcreteElement)。访问者(Visitor)可以是医生或护士,他们可以对不同的科室进行访问,以提供医疗服务。
// 定义一个科室的接口
interface Department {
// 接受访问者的方法
fun accept(visitor: DoctorVisitor)
}
// 具体的内科科室类
class InternalMedicineDepartment : Department() {
override fun accept(visitor: DoctorVisitor) {
// 调用访问者的 visit 方法,并将当前科室作为参数传递
visitor.visit(this)
}
// 可以添加内科科室特有的方法或属性
}
// 具体的外科科室类
class SurgeryDepartment : Department() {
override fun accept(visitor: DoctorVisitor) {
// 调用访问者的 visit 方法,并将当前科室作为参数传递
visitor.visit(this)
}
// 可以添加外科科室特有的方法或属性
}
// 定义一个医生的访问者接口
interface DoctorVisitor {
// 访问内科科室的方法
fun visit(department: InternalMedicineDepartment)
// 访问外科科室的方法
fun visit(department: SurgeryDepartment)
// 可以添加更多科室的访问方法
}
// 具体的全科医生类
class GeneralPractitioner : DoctorVisitor {
override fun visit(department: InternalMedicineDepartment) {
// 全科医生访问内科科室时执行的操作
println("General Practitioner visiting Internal Medicine Department")
// 提供医疗服务
}
override fun visit(department: SurgeryDepartment) {
// 全科医生访问外科科室时执行的操作
println("General Practitioner visiting Surgery Department")
// 提供医疗服务
}
}
// 具体的专科医生类
class Specialist : DoctorVisitor {
override fun visit(department: InternalMedicineDepartment) {
// 专科医生访问内科科室时执行的操作
println("Specialist visiting Internal Medicine Department")
// 提供专业的医疗服务
}
override fun visit(department: SurgeryDepartment) {
// 专科医生访问外科科室时执行的操作
println("Specialist visiting Surgery Department")
// 提供专业的医疗服务
}
}
// 主函数,用于模拟医生和科室之间的交互
fun main() {
// 创建内科科室对象
val internalMedicineDepartment = InternalMedicineDepartment()
// 创建外科科室对象
val surgeryDepartment = SurgeryDepartment()
// 创建全科医生对象
val generalPractitioner = GeneralPractitioner()
// 创建专科医生对象
val specialist = Specialist()
// 全科医生访问内科科室
internalMedicineDepartment.accept(generalPractitioner) // 输出 "General Practitioner visiting Internal Medicine Department"
// 专科医生访问外科科室
surgeryDepartment.accept(specialist) // 输出 "Specialist visiting Surgery Department"
}
在这个例子中,医生和科室之间的关系是通过访问者模式实现的。医生作为访问者,可以对不同的科室进行访问,并根据科室的特点提供相应的医疗服务。这种模式允许医院灵活地添加新的科室或医生,而不需要修改现有的代码结构。同时,它也符合开放封闭原则,新的功能可以通过添加新的类来实现,而不需要修改现有的类。
在Kotlin中,解释器模式(Interpreter Pattern)并不是一种经常使用的设计模式,因为Kotlin作为一种高级语言,已经内置了对许多常见语言结构的支持,如控制流(if, when, for, while等)和数据结构(列表、映射、集合等)
当然如果你确实需要定义一个自己的语言或表达式,并希望为它创建一个解释器,你可以使用解释器模式。
用一个简单的例子来通俗易解地解释解释器模式
// 抽象表达式类
abstract class Expression {
abstract fun interpret(context: Context): Int
}
// 终结符表达式类 - 加法
class AddExpression(private val operand1: Int, private val operand2: Int) : Expression() {
override fun interpret(context: Context): Int {
return operand1 + operand2
}
}
// 终结符表达式类 - 减法
class SubtractExpression(private val operand1: Int, private val operand2: Int) : Expression() {
override fun interpret(context: Context): Int {
return operand1 - operand2
}
}
// 解释器类
class Interpreter {
fun interpret(expression: Expression, context: Context): Int {
return expression.interpret(context)
}
}
// 使用示例
fun main() {
val interpreter = Interpreter()
val addExpr = AddExpression(5, 3)
val subtractExpr = SubtractExpression(addExpr, 2)
val result = interpreter.interpret(subtractExpr, Context())
println("Result: $result") // 输出:Result: 6
}
在这个例子中,我们定义了Expression作为抽象表达式类,AddExpression和SubtractExpression作为终结符表达式类,它们分别实现了加法和减法操作。Context类在这个简单的例子中没有使用,但在更复杂的解释器模式中,它可能包含全局信息,如变量值或函数定义。Interpreter类负责调用表达式的interpret方法来执行操作。
一个请求沿着处理者链传递,直到有一个处理者能够处理它为止。每个处理者都包含对下一个处理者的引用,如果它不能处理请求,就会传递给下一个处理者。这种模式简化了请求发送者和接收者之间的耦合,使得请求可以更容易地在处理者之间传递。
例如:你有一串钥匙,每把钥匙都可能打开一扇门。你按顺序尝试每把钥匙,直到找到能打开门的那一把。这就是责任链模式的工作原理:请求按顺序传递给一系列处理者,直到找到能处理它的处理者。
1、我们定义一个Handler接口,它表示一个处理者(在这个例子中是钥匙)
interface Handler {
fun canHandle(door: Door): Boolean
fun handle(door: Door)
}
2、定义一个Door类,它表示一扇门
class Door {
var isLocked = true
var name: String
constructor(name: String) {
this.name = name
}
fun unlock(key: Key) {
if (key.fits(this)) {
isLocked = false
println("Door $name has been unlocked with key ${key.name}")
} else {
println("Key ${key.name} does not fit door $name")
}
}
}
3、定义一个Key类,它表示一把钥匙,并包含fits方法来检查钥匙是否适合某扇门
class Key(val name: String) {
fun fits(door: Door): Boolean {
// 简单的匹配逻辑,实际应用中可能更复杂
return name == door.name
}
}
4、实现Handler接口的具体类,每个类代表一把具体的钥匙
class KeyHandler(private val key: Key) : Handler {
override fun canHandle(door: Door): Boolean {
return key.fits(door)
}
override fun handle(door: Door) {
if (canHandle(door)) {
door.unlock(key)
} else {
println("This key handler cannot unlock the door.")
}
}
}
5、创建一个Client类来模拟使用责任链来开门的过程
class Client {
fun unlockDoorWithChain(door: Door, handlers: List<Handler>) {
for (handler in handlers) {
if (handler.canHandle(door)) {
handler.handle(door)
return // 找到匹配的钥匙,停止搜索
}
}
println("No suitable key found to unlock the door.")
}
}
6、使用
fun main() {
val door = Door("Front Door")
val keys = listOf(
KeyHandler(Key("Back Door")),
KeyHandler(Key("Front Door")),
KeyHandler(Key("Side Door"))
)
val client = Client()
client.unlockDoorWithChain(door, keys)
}
//输出 Door Front Door has been unlocked with key Front Door
这就是责任链模式一系列的处理者(在这个例子中是钥匙)按顺序尝试处理一个请求(在这个例子中是开锁),直到有一个处理者成功处理请求为止
将请求(或操作)封装为对象,从而允许你使用不同的请求把客户端与接收者解耦。这意味着发出请求的对象不需要知道请求如何被接收、处理或执行。
下面是一个简单的 Kotlin 示例,展示了如何在 Android 应用中使用命令模式。
// 命令接口
interface Command {
fun execute()
}
// 具体命令类
class LightOnCommand(private val light: Light) : Command {
override fun execute() {
light.turnOn()
}
}
class LightOffCommand(private val light: Light) : Command {
override fun execute() {
light.turnOff()
}
}
// 接收者
class Light {
fun turnOn() {
println("Light is on")
}
fun turnOff() {
println("Light is off")
}
}
// 调用者
class SimpleRemoteControl {
private var command: Command? = null
fun setCommand(command: Command) {
this.command = command
}
fun buttonWasPressed() {
command?.execute()
}
}
// 使用示例
fun main() {
val light = Light()
val remoteControl = SimpleRemoteControl()
remoteControl.setCommand(LightOnCommand(light))
remoteControl.buttonWasPressed() // 输出 "Light is on"
remoteControl.setCommand(LightOffCommand(light))
remoteControl.buttonWasPressed() // 输出 "Light is off"
}
在这个示例中,Light 是接收者,它知道如何打开和关闭灯。LightOnCommand 和 LightOffCommand 是具体命令类,它们封装了与灯相关的操作。SimpleRemoteControl 是调用者,它允许我们设置命令并在按钮被按下时执行这些命令。
命令模式在 Android 开发中非常有用,尤其是在处理 UI 事件、后台任务、异步操作或撤销/重做功能时。它提供了很好的灵活性,允许你在运行时改变或添加新的命令,而不需要修改调用者的代码。
定义了一个中介对象来封装一系列对象之间的交互。中介者使得各个对象不需要显式地相互引用,从而降低了耦合度,并且可以独立地改变它们之间的交互。
在 Kotlin 中实现中介者模式通常涉及以下几个步骤:
1、定义中介者接口或类:这个接口或类将包含与所有同事对象通信的方法。
2、定义同事对象接口:这个接口定义了同事对象需要实现的方法,这些方法通常由中介者调用。
3、实现同事对象:这些对象知道中介者的存在,并在需要时与中介者通信。
4、实现中介者:这个类负责协调各个同事对象之间的交互。
例子如下:
1、定义同事接口
interface Colleague {
fun sendMessage(message: String)
fun receiveMessage(message: String)
}
2、实现同事对象
class ConcreteColleagueA(private val mediator: Mediator) : Colleague {
override fun sendMessage(message: String) {
mediator.sendMessage(this, message)
}
override fun receiveMessage(message: String) {
println("同事A收到消息: $message")
}
}
class ConcreteColleagueB(private val mediator: Mediator) : Colleague {
override fun sendMessage(message: String) {
mediator.sendMessage(this, message)
}
override fun receiveMessage(message: String) {
println("同事B收到消息: $message")
}
}
3、定义中介者对象
interface Mediator {
fun sendMessage(colleague: Colleague, message: String)
}
4、实现中介者对象
class ConcreteMediator : Mediator {
private val colleagueA = ConcreteColleagueA(this)
private val colleagueB = ConcreteColleagueB(this)
override fun sendMessage(colleague: Colleague, message: String) {
when (colleague) {
colleagueA -> colleagueB.receiveMessage(message)
colleagueB -> colleagueA.receiveMessage(message)
else -> println("未知的同事")
}
}
}
5、使用中介者
fun main() {
val mediator = ConcreteMediator()
mediator.sendMessage(mediator.colleagueA, "你好,我是A!")
mediator.sendMessage(mediator.colleagueB, "你好,我是B!")
}
在这个例子中,ConcreteColleagueA 和 ConcreteColleagueB 是同事对象,它们通过 ConcreteMediator 中介者来交互。当 ConcreteColleagueA 发送消息时,ConcreteMediator 会确保 ConcreteColleagueB 收到这条消息,反之亦然。这样,同事对象之间的通信被封装在中介者中,降低了它们之间的耦合度。