/**
* 软件设计模式是可复用的、经过验证的解决方案,用于解决软件开发中常见的问题。设计模式通常被分类为以下三大类:
*
* ### 1. 创建型模式
*
* 这些模式主要关注对象的创建过程,目的是将对象的创建与使用分离,从而提高系统的灵活性和可扩展性。
*
* - **1.1、单例模式(Singleton Pattern)**:保证一个类只有一个实例,并提供一个全局访问点。
* - **1.2、工厂方法模式(Factory Method Pattern)**:定义一个创建对象的接口,但由子类决定实例化哪个类。
* - **1.3、抽象工厂模式(Abstract Factory Pattern)**:提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。
* - **1.4、生成器模式(Builder Pattern)**:将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。
* - **1.5、原型模式(Prototype Pattern)**:通过复制现有实例来创建对象,而不是通过类构造函数实例化。
*
* ### 2. 结构型模式
*
* 这些模式关注类和对象组成成更大结构的方式,帮助确保当一个系统的某个部分变化时,这个系统的整体结构不会受到太大影响。
*
* - **2.1、适配器模式(Adapter Pattern)**:将一个类的接口转换成客户期望的另一个接口,使原本接口不兼容的类可以一起工作。
* - **2.2、桥接模式(Bridge Pattern)**:将抽象部分与其实现部分分离,使它们可以独立变化。
* - **2.3、组合模式(Composite Pattern)**:将对象组合成树状结构以表示“部分-整体”的层次结构,使得客户端对单个对象和组合对象的使用具有一致性。
* - **2.4、装饰器模式(Decorator Pattern)**:动态地给对象添加一些额外的职责,功能扩展方面比继承更灵活。
* - **2.5、外观模式(Facade Pattern)**:为子系统中的一组接口提供一个一致的界面,使得子系统更容易使用。
* - **2.6、享元模式(Flyweight Pattern)**:通过共享来有效地支持大量细粒度对象。
* - **2.7、代理模式(Proxy Pattern)**:为其他对象提供一种代理以控制对这个对象的访问。
*
* ### 3. 行为型模式
*
* 这些模式关注算法和对象间职责的分配,描述对象之间如何协作以完成任务。
*
* - **3.1、责任链模式(Chain of Responsibility Pattern)**:为请求创建一个接收者对象的链,解决请求的责任由链中的某个对象来承担。
* - **3.2、命令模式(Command Pattern)**:将请求封装为对象,从而可以使用不同的请求、队列或日志来参数化其他对象。
* - **3.3、解释器模式(Interpreter Pattern)**:为语言创建解释器,定义语言的文法,并解析语言中的句子。
* - **3.4、迭代器模式(Iterator Pattern)**:提供一种方法访问集合对象中的各个元素,而又不需要暴露该对象的内部表示。
* - **3.5、中介者模式(Mediator Pattern)**:用一个中介对象封装一系列对象的交互,使对象不需要显示地互相引用,从而使其耦合松散。
* - **3.6、备忘录模式(Memento Pattern)**:在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
* - **3.7、观察者模式(Observer Pattern)**:定义对象间的一种一对多的依赖关系,使得当一个对象的状态发生改变时,其相关依赖对象皆得到通知并被自动更新。
* - **3.8、状态模式(State Pattern)**:允许对象在内部状态改变时改变其行为,看起来就像改变了其类。
* - **3.9、策略模式(Strategy Pattern)**:定义一系列算法,把它们一个个封装起来,并且它们可以相互替换。
* - **3.10、模板方法模式(Template Method Pattern)**:在一个方法中定义一个算法的骨架,而将一些步骤推迟到子类中。
* - **3.11、访问者模式(Visitor Pattern)**:表示一个作用于某对象结构中的各元素的操作,可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
*
* 这些模式并不穷尽,但提供了一个在多种情况下广泛使用的解决方案集合。设计模式是软件工程中宝贵的工具,能帮助开发者设计出更好的系统架构。
*/
class DesignMode {
/**
* #############################################################################################
* ### 1. 创建型模式
* 1.1、单例模式:懒汉式(by lazy 双重检查锁)
*/
class LazySingleton private constructor() {
// 单例对象
companion object {
// 使用 Kotlin 的 `by lazy` 委托来实现懒汉式单例模式
val instance: LazySingleton by lazy {
LazySingleton()
}
}
// 其他成员变量和方法
fun doSomething() {
println("Doing something")
}
}
@Test
fun testLazySingleton() {
val singleton = LazySingleton.instance
singleton.doSomething()
}
/**
* ### 1. 创建型模式
* 1.1、单例模式:饿汉式
*/
object EagerSingleton {
// 初始化代码块
init {
println("EagerSingleton initialized")
}
// 其他成员变量和方法
fun doSomething() {
println("Doing something")
}
}
@Test
fun testEagerSingleton() {
val singleton = EagerSingleton
singleton.doSomething()
}
/**
* ### 1. 创建型模式
* 1.2、工厂方法模式(Factory Method Pattern)
* 概述
* 工厂方法模式(Factory Method Pattern)是一种创建型设计模式,
* 它定义了一个创建对象的接口,但由子类决定实例化哪个类。工厂方法使得一个类的实例化延迟到其子类。
*
* 应用场景
* 工厂方法模式适用于以下场景:
*
* 当一个类不知道它所必须创建的对象的类时:工厂方法模式将对象的创建延迟到子类,通过多态机制,客户代码可以与具体对象实现解耦。
*
* 一个类希望由它的子类来指定创建哪个类时:通过定义接口,允许子类提供具体的实现。
*
* 在类中需要灵活地添加新的对象时:可以通过添加新的具体工厂和产品来扩展系统,而不影响现有代码结构。
*
* 隐藏对象的创建逻辑:工厂方法能隐藏对象的创建细节,客户端无需知道实例化的具体类。
*
* 优点
* 解耦:客户端代码与具体产品类解耦,代码更加灵活。
* 扩展性:添加新产品时,只需增加相应的具体产品类和工厂类,符合开闭原则。
* 单一职责:工厂方法模式将产品的创建与使用分离,符合单一职责原则。
* 缺点
* 类的数量增加:每新增一个产品都需要增加相应的工厂类,导致类数量增加。
* 复杂性增加:对于简单对象的创建,使用工厂方法模式可能会导致不必要的复杂性。
*/
// 产品接口
interface Product {
fun use(): String
}
// 具体产品A
class ConcreteProductA : Product {
override fun use(): String {
return "Using ConcreteProductA"
}
}
// 具体产品B
class ConcreteProductB : Product {
override fun use(): String {
return "Using ConcreteProductB"
}
}
// 工厂接口
abstract class Creator {
abstract fun createProduct(): Product
}
// 具体工厂A
class ConcreteCreatorA : Creator() {
override fun createProduct(): Product {
return ConcreteProductA()
}
}
// 具体工厂B
class ConcreteCreatorB : Creator() {
override fun createProduct(): Product {
return ConcreteProductB()
}
}
// 使用示例
@Test
fun testFactoryMethodPattern() {
val creatorA: Creator = ConcreteCreatorA()
val productA: Product = creatorA.createProduct()
println(productA.use()) // 输出: Using ConcreteProductA
val creatorB: Creator = ConcreteCreatorB()
val productB: Product = creatorB.createProduct()
println(productB.use()) // 输出: Using ConcreteProductB
}
/**
* ### 1. 创建型模式
* 1.3、抽象工厂模式(Abstract Factory Pattern)
*
* 概述
* 抽象工厂模式(Abstract Factory Pattern)是一种创建型设计模式,
* 它提供了一种创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
* 通过使用抽象工厂模式,客户端可以通过使用抽象接口来创建一组相关或依赖的对象,而不需要关心具体对象的类。
*
* 应用场景
* 需要创建一组相关或依赖的对象时:适用于需要创建不同风格或系列的产品(如各类操作系统或主题)的场景。
* 系统独立于产品的创建和构成时:当系统无需知道产品如何被创建或构成时,抽象工厂允许在客户端代码中使用接口,隐藏复杂的创建过程。
* 需要提供一组产品的多种变体时:当多个产品族需要支持时(如上例中的Windows和Mac),可以很方便地通过工厂的扩展来实现。
*
* 优点
* 抽象隔离:客户端通过抽象接口使用产品,无需关心具体实现。
* 一致性:确保同一工厂生产的产品是兼容的,使用起来更一致。
* 扩展性:可以通过增加新的具体工厂和产品族来支持更多的产品变体。
*
* 缺点
* 复杂性上升:增加系统中的类和接口,复杂性上升。
* 扩展产品族困难:如果需要增加新的产品种类,必须修改抽象工厂及其所有的具体工厂。
*/
// 抽象产品A:按钮
interface Button {
fun click(): String
}
// 抽象产品B:文本框
interface Textbox {
fun type(): String
}
// 具体产品A1:Windows按钮
class WindowsButton : Button {
override fun click(): String {
return "Windows Button Clicked"
}
}
// 具体产品B1:Windows文本框
class WindowsTextbox : Textbox {
override fun type(): String {
return "Windows Textbox Typed"
}
}
// 具体产品A2:Mac按钮
class MacButton : Button {
override fun click(): String {
return "Mac Button Clicked"
}
}
// 具体产品B2:Mac文本框
class MacTextbox : Textbox {
override fun type(): String {
return "Mac Textbox Typed"
}
}
// 抽象工厂
interface GUIFactory {
fun createButton(): Button
fun createTextbox(): Textbox
}
// 具体工厂1:Windows工厂
class WindowsFactory : GUIFactory {
override fun createButton(): Button {
return WindowsButton()
}
override fun createTextbox(): Textbox {
return WindowsTextbox()
}
}
// 具体工厂2:Mac工厂
class MacFactory : GUIFactory {
override fun createButton(): Button {
return MacButton()
}
override fun createTextbox(): Textbox {
return MacTextbox()
}
}
// 使用示例
@Test
fun testAbstractFactoryPattern() {
// 选择使用Windows风格的工厂
val guiFactory: GUIFactory = WindowsFactory()
val button: Button = guiFactory.createButton()
val textbox: Textbox = guiFactory.createTextbox()
println(button.click()) // 输出: Windows Button Clicked
println(textbox.type()) // 输出: Windows Textbox Typed
// 选择使用Mac风格的工厂
val macFactory: GUIFactory = MacFactory()
val macButton: Button = macFactory.createButton()
val macTextbox: Textbox = macFactory.createTextbox()
println(macButton.click()) // 输出: Mac Button Clicked
println(macTextbox.type()) // 输出: Mac Textbox Typed
}
/**
* ### 1. 创建型模式
* 1.4、生成器模式(Builder Pattern)**:将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。
*
* 概述
* 生成器模式(Builder Pattern)是一种创建型设计模式,旨在通过提供一种方法来构建复杂对象的不同表示,并将对象的构建过程与其表示分离。这样,同样的构建过程可以创建不同的表示。
* 生成器模式特别适合于需要逐步构建复杂对象,或者希望通过多个步骤来构建对象时使用。
*
* 应用场景
* 需要创建复杂对象:当一个对象包含大量的组件或需要复杂的配置时,生成器模式非常适合。
* 构造过程独立于表示时:当同样的构造过程可以创建不同的表示时,生成器模式使得构造过程与其表示分离。
* 需要灵活构建对象时:生成器模式允许在构建过程中以不同的顺序或条件来构建对象。
*
* 优点
* 单一职责原则:生成器将复杂对象的创建代码与实际的业务逻辑代码分离。
* 更好的控制:可以更清晰地控制对象的构建过程。
* 可变性和扩展性:易于增加新的构建配置或步骤而不影响现有代码。
*
* 缺点
* 复杂性增加:对于简单对象的创建,使用生成器模式可能会显得过于复杂。
* 可能导致过多的类:可能需要为每个复杂对象的每个变体定义一个具体生成器。
*
* 生成器模式非常适合在需要创建复杂对象的场景中使用,特别是在构造对象的步骤非常多、需要灵活变化或对象配置复杂时。这种模式提供了更好的代码可读性和维护性。
*/
// 产品:房子
class House {
var walls: String = ""
var doors: String = ""
var roof: String = ""
override fun toString(): String {
return "House(walls='$walls', doors='$doors', roof='$roof')"
}
}
// 抽象生成器
interface HouseBuilder {
fun buildWalls()
fun buildDoors()
fun buildRoof()
fun getResult(): House
}
// 具体生成器:木屋
class WoodenHouseBuilder : HouseBuilder {
private val house = House()
override fun buildWalls() {
house.walls = "Wooden Walls"
}
override fun buildDoors() {
house.doors = "Wooden Doors"
}
override fun buildRoof() {
house.roof = "Wooden Roof"
}
override fun getResult(): House {
return house
}
}
// 具体生成器:石屋
class StoneHouseBuilder : HouseBuilder {
private val house = House()
override fun buildWalls() {
house.walls = "Stone Walls"
}
override fun buildDoors() {
house.doors = "Stone Doors"
}
override fun buildRoof() {
house.roof = "Stone Roof"
}
override fun getResult(): House {
return house
}
}
// 指挥者
class Director(private val builder: HouseBuilder) {
fun construct() {
builder.buildWalls()
builder.buildDoors()
builder.buildRoof()
}
}
// 使用示例
@Test
fun testBuilderPattern() {
val woodenBuilder = WoodenHouseBuilder()
val director1 = Director(woodenBuilder)
director1.construct()
val woodenHouse = woodenBuilder.getResult()
println(woodenHouse) // 输出: House(walls='Wooden Walls', doors='Wooden Doors', roof='Wooden Roof')
val stoneBuilder = StoneHouseBuilder()
val director2 = Director(stoneBuilder)
director2.construct()
val stoneHouse = stoneBuilder.getResult()
println(stoneHouse) // 输出: House(walls='Stone Walls', doors='Stone Doors', roof='Stone Roof')
}
/**
* ### 1. 创建型模式
* 1.5、原型模式(Prototype Pattern)**:通过复制现有实例来创建对象,而不是通过类构造函数实例化。
* 原型模式允许对象在运行时动态地选择创建的对象类型,对应的类不必与客户端代码紧密耦合。
* 在 Kotlin 中,可以通过实现 Cloneable 接口及其 clone() 方法来实现原型模式。这种模式在需要大量创建相似对象时尤其有用。
*
* 应用场景
* 需要频繁创建类的实例,并且每个实例都与现有实例相似时:原型模式可以避免创建新实例时的昂贵代价。
* 系统使用的类实例只差别其状态组合时:如果每个实例的差异仅仅在于属性值的不同,可以通过克隆现有实例来创建新实例。
* 减少子类的创建:通过克隆实现创建方式,可以减少创建不同对象时的子类数量。
* 配置对象或资源的共享:当要在不同对象中共享某些配置或资源时,原型模式可以很方便地创建相似对象。
*
* 优点
* 减少创建成本:通过克隆现有对象,避免了对象的重复创建。
* 动态扩展:可以在运行时动态创建对象。
* 简化对象创建:通过克隆,避免了复杂的构造函数调用。
*
* 缺点
* 实现复杂性:必须处理对象深拷贝和浅拷贝之间的区别。
* 难以实现深度克隆:对于复杂对象图,可能需要额外的代码来实现深度克隆。
*
* 通过以上描述和代码示例,你可以了解如何在 Kotlin 中实现原型模式及其适用场景。这种模式对于创建复杂和相似的对象非常有用,尤其是在对象创建成本较高的情况下。
*/
// 原型接口
abstract class Shape : Cloneable {
var x: Int = 0
var y: Int = 0
var color: String = ""
abstract fun draw()
// 克隆方法
public override fun clone(): Shape {
return super.clone() as Shape
}
}
// 具体原型1:圆形
class Circle(var radius: Int) : Shape() {
override fun draw() {
println("Drawing Circle at ($x, $y) with radius $radius and color $color")
}
// 克隆方法
override fun clone(): Circle {
return super.clone() as Circle
}
}
// 具体原型2:矩形
class Rectangle(var width: Int, var height: Int) : Shape() {
override fun draw() {
println("Drawing Rectangle at ($x, $y) with width $width, height $height and color $color")
}
// 克隆方法
override fun clone(): Rectangle {
return super.clone() as Rectangle
}
}
// 使用示例
@Test
fun testPrototypePattern() {
// 创建一个圆形实例
val originalCircle = Circle(10)
originalCircle.x = 5
originalCircle.y = 5
originalCircle.color = "Red"
originalCircle.draw()
// 克隆圆形实例
val clonedCircle = originalCircle.clone()
clonedCircle.color = "Blue" // 修改颜色
clonedCircle.draw()
// 创建一个矩形实例
val originalRectangle = Rectangle(6, 4)
originalRectangle.x = 2
originalRectangle.y = 2
originalRectangle.color = "Green"
originalRectangle.draw()
// 克隆矩形实例
val clonedRectangle = originalRectangle.clone()
clonedRectangle.width = 8 // 修改宽度
clonedRectangle.draw()
}
/**
* #############################################################################################
* ### 2. 结构型模式
* 2.1、适配器模式(Adapter Pattern)**:将一个类的接口转换成客户期望的另一个接口,使原本接口不兼容的类可以一起工作。
*
* 应用场景
* Legacy 系统集成:需要将老旧系统中的类与新系统中的类集成时,适配器可以帮助转换接口。
* 接口不兼容:当两个类的接口不兼容但需要协作时,使用适配器模式将一个类的接口转换为另一个类的接口。
* 复用现有类:在不修改现有类的情况下,通过适配其接口来复用该类。
*
* 优点
* 单一职责原则:可以将接口或数据转换的代码与应用程序的主业务逻辑分离。
* 开闭原则:无需修改现有代码即可引入新的适配器。
*
* 缺点
* 复杂性增加:在某些情况下,增加了代码的复杂性。
* 过度使用适配器可能导致系统设计混乱:大量使用适配器可能导致系统变得难以理解和维护。
*
* 适配器模式在需要将不兼容的接口整合在一起时非常有用,很常用于旧系统的集成和重用现有类。通过代码示例,你可以看到适配器如何帮助将老旧播放器接口与新系统兼容。
*/
// 目标接口:新的播放器接口
interface MediaPlayer {
fun play(audioType: String, fileName: String)
}
// 适配者类:旧的 VLC 播放器
class VlcPlayer {
fun playVlc(fileName: String) {
println("Playing vlc file. Name: $fileName")
}
}
// 适配器类:使 VlcPlayer 适配 MediaPlayer 接口
class MediaAdapter(private val vlcPlayer: VlcPlayer) : MediaPlayer {
override fun play(audioType: String, fileName: String) {
if (audioType.equals("vlc", ignoreCase = true)) {
vlcPlayer.playVlc(fileName)
} else {
println("Invalid media type: $audioType")
}
}
}
// 使用示例
class AudioPlayer : MediaPlayer {
private var mediaAdapter: MediaAdapter? = null
override fun play(audioType: String, fileName: String) {
if (audioType.equals("vlc", ignoreCase = true)) {
mediaAdapter = MediaAdapter(VlcPlayer())
mediaAdapter?.play(audioType, fileName)
} else {
println("Playing mp3 file. Name: $fileName")
}
}
}
// 使用示例
@Test
fun testAdapterPattern() {
val audioPlayer = AudioPlayer()
audioPlayer.play("mp3", "song.mp3") // 输出: Playing mp3 file. Name: song.mp3
audioPlayer.play("vlc", "movie.vlc") // 输出: Playing vlc file. Name: movie.vlc
}
/**
* ### 2. 结构型模式
* 2.2、桥接模式(Bridge Pattern)**:将抽象部分与其实现部分分离,使它们可以独立变化。
* 桥接模式的核心思想是将类的接口(抽象部分)与类的实现(实现部分)解耦,这样两者可以独立地进行开发、扩展和修改。
*
* 应用场景
* 避免类的爆炸性增长:如果一个系统中存在多维度的变化,例如形状和绘图API,桥接模式可减少子类的数量。
* 希望将抽象和实现分离以便独立扩展:当一个类存在多个可能会变化的维度,并希望独立扩展这些维度时,桥接模式非常有用。
* 需要在运行时切换实现:桥接模式可以在运行时动态切换实现,因为抽象和实现之间是通过组合关联的。
*
* 优点
* 分离接口及其实现:允许在不影响用户的情况下更换和修改实现。
* 提高可扩展性:可以独立地扩展抽象部分和实现部分。
* 降低类的数量:通过组合而非继承来实现多维度的变化。
*
* 缺点
* 复杂性增加:引入了额外的抽象层,可能导致系统复杂性增加。
* 可能导致过度设计:在问题不复杂的情况下使用桥接模式可能是过度设计。
*
* 桥接模式通过将抽象和实现分离,提供了一种灵活的扩展方式,使得系统在面对多维度变化时更具扩展性和灵活性,非常适合需要解耦和独立扩展的场景。
*/
// 实现化接口:定义绘制行为
interface DrawingAPI {
fun drawCircle(x: Double, y: Double, radius: Double)
fun drawSquare(x: Double, y: Double, side: Double)
}
// 具体实现化1:SVG绘图API
class SvgDrawingAPI : DrawingAPI {
override fun drawCircle(x: Double, y: Double, radius: Double) {
println("SVG: Drawing a circle at ($x, $y) with radius $radius")
}
override fun drawSquare(x: Double, y: Double, side: Double) {
println("SVG: Drawing a square at ($x, $y) with side $side")
}
}
// 具体实现化2:Canvas绘图API
class CanvasDrawingAPI : DrawingAPI {
override fun drawCircle(x: Double, y: Double, radius: Double) {
println("Canvas: Drawing a circle at ($x, $y) with radius $radius")
}
override fun drawSquare(x: Double, y: Double, side: Double) {
println("Canvas: Drawing a square at ($x, $y) with side $side")
}
}
// 抽象化:形状
abstract class ShapeA(protected val drawingAPI: DrawingAPI) {
abstract fun draw() // 由具体实现所引入
abstract fun resizeByPercentage(percentage: Double)
}
// 细化抽象化:圆形
class CircleA(
private var x: Double,
private var y: Double,
private var radius: Double,
drawingAPI: DrawingAPI
) : ShapeA(drawingAPI) {
override fun draw() {
drawingAPI.drawCircle(x, y, radius)
}
override fun resizeByPercentage(percentage: Double) {
radius *= (1 + percentage / 100)
}
}
// 细化抽象化:正方形
class SquareA(
private var x: Double,
private var y: Double,
private var side: Double,
drawingAPI: DrawingAPI
) : ShapeA(drawingAPI) {
override fun draw() {
drawingAPI.drawSquare(x, y, side)
}
override fun resizeByPercentage(percentage: Double) {
side *= (1 + percentage / 100)
}
}
// 使用示例
@Test
fun testBridgePattern() {
val shapes = listOf(
CircleA(1.0, 2.0, 3.0, SvgDrawingAPI()),
SquareA(5.0, 7.0, 10.0, CanvasDrawingAPI())
)
shapes.forEach { shape ->
shape.draw()
shape.resizeByPercentage(10.0)
shape.draw()
}
}
/**
* ### 2. 结构型模式
* 2.3、组合模式(Composite Pattern)**:将对象组合成树状结构以表示“部分-整体”的层次结构。
* 组合模式使得客户端可以统一地处理单个对象和组合对象,这在处理树形结构时非常有用。
* 这种模式常用于表示类似文件系统的结构或GUI中的组件容器结构。
*
* 应用场景
* 文件系统结构:文件系统中的目录可以包含文件或其他目录,组合模式很适合用于表示这种树形结构。
* 图形用户界面(GUI):在GUI框架中,容器可以包含按钮、文本框等控件,也可以包含其他容器。
* 组织结构:在组织结构中,部门可以包含子部门或员工,可以使用组合模式来表示这种层次结构。
*
* Android AOSP 中的应用
* Android 的 AOSP(Android Open Source Project)中广泛使用了组合模式,特别是在 UI 组件系统中:
* - View 和 ViewGroup:
* View 是一个组件,而 ViewGroup 是一个容器,可以包含多个 View 或 ViewGroup。
* ViewGroup 是一个经典的组合模式应用,允许将多个视图组合在一起并作为一个整体进行操作。
*
* - RecyclerView 和 Adapter 模式:
* 虽然主要是适配器模式,但 RecyclerView 也体现了组合模式的思想,
* 它可以包含多种类型的视图组件,并以统一的方式进行管理。
*
* 组合模式非常适合用于需要处理树形结构的场景,允许以统一的方式管理单个对象和组合对象,
* 这种特性在复杂结构的代码中非常有用。通过统一的接口,客户端代码可以透明地处理不同级别的对象,从而简化了系统设计。
*/
// 组件接口:定义图形的公共方法
interface Graphic {
fun draw() // 绘制图形
}
// 叶子类:圆形
class CircleB(private val name: String) : Graphic {
override fun draw() {
println("Drawing Circle: $name")
}
}
// 叶子类:正方形
class SquareB(private val name: String) : Graphic {
override fun draw() {
println("Drawing Square: $name")
}
}
// 组合类:图形组合
class CompositeGraphic : Graphic {
private val graphics = mutableListOf<Graphic>()
fun add(graphic: Graphic) {
graphics.add(graphic)
}
fun remove(graphic: Graphic) {
graphics.remove(graphic)
}
override fun draw() {
for (graphic in graphics) {
graphic.draw()
}
}
}
// 使用示例
@Test
fun testCompositePattern() {
// 创建叶子对象
val circle1 = CircleB("Circle 1")
val circle2 = CircleB("Circle 2")
val square1 = SquareB("Square 1")
// 创建组合对象
val compositeGraphic = CompositeGraphic()
compositeGraphic.add(circle1)
compositeGraphic.add(circle2)
compositeGraphic.add(square1)
// 绘制组合对象
compositeGraphic.draw()
}
/**
* ### 2. 结构型模式
* 2.4、装饰器模式(Decorator Pattern)**:动态地给对象添加一些额外的职责,功能扩展方面比继承更灵活。
*
* 应用场景
* 增加功能:在不改变现有类的情况下给对象添加功能,尤其是在类不能被修改或者继承时。
* 职责动态组合:允许通过一些基本职责的组合来实现复杂功能,可以根据需要动态添加职责。
* 替代子类扩展:通过组合而非继承的方式扩展对象的行为。
*
* Android AOSP 中的应用
* Android 的 AOSP 中使用了装饰器模式,特别是在以下场景:
* InputStream 和 OutputStream:在 Java I/O 中,InputStream 和 OutputStream 的一些子类
* (如 BufferedInputStream、DataInputStream)实际上就是装饰器。这些类为基础的流对象提供额外的功能(如缓冲和数据类型转换)。
* 视图包装:在 Android 中,LayoutInflater 的 Factory 和 Factory2 接口允许开发者在视图创建时进行额外的处理,某种程度上提供了一种装饰视图的机制。
* 通过使用装饰器模式,Android 能够在不修改已有代码的情况下,动态地给对象添加功能。这种灵活的设计使得系统更容易扩展和维护。
*/
// 组件接口
interface TextView {
fun draw() // 绘制方法
}
// 具体组件:基本文本框
class BasicTextView : TextView {
override fun draw() {
println("Drawing basic text view")
}
}
// 装饰者类
open class TextViewDecorator(private val textView: TextView) : TextView {
override fun draw() {
textView.draw()
}
}
// 具体装饰者:增加边框功能
class BorderTextViewDecorator(textView: TextView) : TextViewDecorator(textView) {
override fun draw() {
super.draw()
drawBorder()
}
private fun drawBorder() {
println("Adding border")
}
}
// 具体装饰者:增加滚动条功能
class ScrollBarTextViewDecorator(textView: TextView) : TextViewDecorator(textView) {
override fun draw() {
super.draw()
drawScrollBar()
}
private fun drawScrollBar() {
println("Adding scroll bar")
}
}
// 使用示例
@Test
fun testDecoratorPattern() {
// 创建基本文本框
val basicTextView = BasicTextView()
// 装饰文本框,增加边框
val borderedTextView = BorderTextViewDecorator(basicTextView)
// 装饰文本框,增加滚动条
val scrollableBorderTextView = ScrollBarTextViewDecorator(borderedTextView)
// 绘制
scrollableBorderTextView.draw()
}
/**
* ### 2. 结构型模式
* 2.5、外观模式(Facade Pattern)**:为子系统中的一组接口提供一个一致的界面,使得子系统更容易使用。
* 它隐藏了系统的复杂性,通过提供一个更高层次的接口,简化了客户端与子系统之间的交互。
* 这种模式非常适合当你需要为一个复杂的库或框架提供一个简单的接口时使用。
*
* 应用场景
* 简化复杂系统:为一个复杂的子系统提供一个简单的接口。
* 松散耦合:将客户端与复杂子系统分离,从而降低耦合。
* 更好的维护性:通过外观模式,子系统的修改不会影响到客户端。
*
* Android AOSP 中的应用
* 在 Android 的 AOSP 中,外观模式也有一些典型应用:
* MediaPlayer API:MediaPlayer 封装了视频播放的复杂性,为开发者提供了一个简单的接口来播放音频和视频。它内部处理线程、缓冲、音频焦点等多个子系统的交互。
* NotificationManager:NotificationManager 通过封装复杂的通知机制,提供了简单的方法来创建和管理通知。
* Location Services:Android 的位置服务提供了一套简单的接口,用于处理复杂的定位子系统,包括 GPS、网络定位等。
*
* 外观模式在这些地方的应用,使得复杂的框架和库对开发者更加友好和易用,不用关心底层的复杂实现,专注于上层功能的实现。
*
* 示例
* 假设我们正在开发一个简单的家庭影院系统,它包含多个组件(如 DVD 播放器、音响、投影仪)。
* 我们可以使用外观模式来提供一个简单的接口,以便一键启动或关闭整个家庭影院系统。
*/
// 子系统类:DVD播放器
class DvdPlayer {
fun on() = println("DVD Player is on")
fun off() = println("DVD Player is off")
fun play(movie: String) = println("Playing movie: $movie")
}
// 子系统类:音响
class Amplifier {
fun on() = println("Amplifier is on")
fun off() = println("Amplifier is off")
fun setVolume(level: Int) = println("Setting volume to $level")
}
// 子系统类:投影仪
class Projector {
fun on() = println("Projector is on")
fun off() = println("Projector is off")
fun wideScreenMode() = println("Projector in widescreen mode")
}
// 外观类:家庭影院外观
class HomeTheaterFacade(
private val dvdPlayer: DvdPlayer,
private val amplifier: Amplifier,
private val projector: Projector
) {
fun watchMovie(movie: String) {
println("Get ready to watch a movie...")
dvdPlayer.on()
dvdPlayer.play(movie)
amplifier.on()
amplifier.setVolume(5)
projector.on()
projector.wideScreenMode()
}
fun endMovie() {
println("Shutting movie theater down...")
dvdPlayer.off()
amplifier.off()
projector.off()
}
}
// 使用示例
@Test
fun testFacadePattern() {
val dvdPlayer = DvdPlayer()
val amplifier = Amplifier()
val projector = Projector()
val homeTheater = HomeTheaterFacade(dvdPlayer, amplifier, projector)
homeTheater.watchMovie("Inception")
homeTheater.endMovie()
}
/**
* ### 2. 结构型模式
* 2.6、享元模式(Flyweight Pattern)**:通过共享来有效地支持大量细粒度对象。
* 它通过共享来减少创建对象的数量,从而减少内存占用和提高性能。这对于内存约束的环境尤其有用。
*
* 应用场景
* 需要大量细粒度对象:如果一个应用程序需要大量细粒度对象,而这些对象的创建代价很高。
* 对象的多数状态可以被外部化:如果对象的大部分状态都可以被外部化,这样就可以使用享元模式来进行对象共享。
* 系统中存在大量相似对象:这些对象可以被利用共享的方式节省内存。
*
* Android AOSP 中的应用
* 在 Android 的 AOSP 中,享元模式也有一些典型应用:
* - String Pool(字符串池):Java 中的字符串池是享元模式的一个经典应用。当你使用字符串字面量时,如果相同的字符串已经存在于池中,就会被复用。
* - Drawable 缓存:在 Android 系统中,Drawable 对象可能被多次使用和缓存,以减少内存开销。这可以被视作享元模式的一种实现。
* - Typeface 缓存:Typeface 在 Android 中通常被缓存。如果相同的字体已经加载,则不需要重新加载,直接使用缓存的实例。
* 享元模式通过减少对象的重复创建,能够在 Android 系统中有效地节省内存和提高性能。这对于资源有限的移动设备来说尤其重要。
*
* 示例
* 假设我们正在开发一个文本编辑器,我们需要频繁地渲染和操作不同的字符。使用享元模式可以共享字符对象,减少内存使用。
* 在这个示例中,字符对象 (Character) 被共享,每种字符只会创建一次,即使它们在不同的位置被使用。
*/
// 享元接口
interface Flyweight {
fun render(x: Int, y: Int) // 渲染方法,使用外部状态(位置)
}
// 具体享元类:字符对象
class Character(private val symbol: Char) : Flyweight {
override fun render(x: Int, y: Int) {
println("Rendering character '$symbol' at position ($x, $y)")
}
}
// 享元工厂
class FlyweightFactory {
private val flyweights = mutableMapOf<Char, Flyweight>()
fun getFlyweight(symbol: Char): Flyweight {
return flyweights.computeIfAbsent(symbol) { Character(it) } // 在给定键不存在于映射中时计算其值,并将其添加到映射中
}
}
// 使用示例
@Test
fun testFlyweightPattern() {
val factory = FlyweightFactory()
// 渲染字符'X'在不同位置
val charX1 = factory.getFlyweight('X')
charX1.render(10, 10)
val charX2 = factory.getFlyweight('X')
charX2.render(20, 20)
// 渲染字符'O'在不同位置
val charO1 = factory.getFlyweight('O')
charO1.render(30, 30)
val charO2 = factory.getFlyweight('O')
charO2.render(40, 40)
}
/**
* ### 2. 结构型模式
* 2.7、代理模式(Proxy Pattern)**:为其他对象提供一种代理以控制对这个对象的访问。
* 代理模式通常用于延迟实例化、控制访问权限、日志记录、性能优化等场景
*
* 应用场景
* 延迟加载:如果一个对象的创建非常昂贵且资源密集,可以使用代理来延迟对象的创建。
* 访问控制:代理可以控制对目标对象的访问权限,增强安全性。
* 日志记录和监控:代理可以在访问真实对象之前或之后进行日志记录或计量。
* 远程代理:代理对象位于不同的地址空间,可以处理网络通信。
*
* Android AOSP 中的应用
* 在 Android 的 AOSP 中,代理模式也有许多应用:
* - Binder IPC 机制:Android 使用 Binder 作为进程间通信的基础设施。Binder 的代理(Proxy)对象可以代表服务的实际实现,允许进程间轻松通信。
* - 动态代理:Android 中的许多服务(如 ActivityManager 等)使用动态代理来创建服务接口的实现。这些代理控制对系统服务的访问。
* - ContentProvider:使用代理模式来控制对数据的访问。ContentProvider 提供接口来访问不同的应用程序数据,并在其方法中实现了访问控制。
* 代理模式在 Android 中通过提供间接访问来增强灵活性和控制能力,使得系统和应用程序的设计更加模块化和可维护。
*
* 示例
* 假设我们正在开发一个图像查看器应用,该应用需要延迟加载大图像以提高用户界面响应速度。我们可以使用代理模式来实现这一点。
* 在这个示例中:
*
* Image 是一个接口,RealImage 是实现这个接口的类,表示实际加载的图像。
* ProxyImage 实现了 Image 接口,但它在实际需要显示图像时才创建 RealImage 对象,从而实现延迟加载。
*/
// 定义一个接口,表示图片
interface Image {
fun display() // 显示图片的方法
}
// 真实主题类,表示实际的图片
class RealImage(private val fileName: String) : Image {
init {
loadFromDisk() // 模拟从磁盘加载图像
}
override fun display() {
println("Displaying $fileName")
}
private fun loadFromDisk() {
println("Loading $fileName from disk...")
}
}
// 代理类,控制对 RealImage 的访问
class ProxyImage(private val fileName: String) : Image {
private var realImage: RealImage? = null
override fun display() {
if (realImage == null) {
realImage = RealImage(fileName) // 延迟加载
}
realImage?.display()
}
}
// 使用示例
@Test
fun testProxyPattern() {
val image1 = ProxyImage("photo1.jpg")
val image2 = ProxyImage("photo2.jpg")
// 图片尚未加载,调用显示方法会触发加载
image1.display()
// 再次调用,图片已经加载,不需要重复加载
image1.display()
image2.display()
image2.display()
}
/**
* #############################################################################################
* ### 3. 行为型模式
* 3.1、责任链模式(Chain of Responsibility Pattern)**:为请求创建一个接收者对象的链,解决请求的责任由链中的某个对象来承担。
* 它允许多个对象都有机会处理请求,从而避免请求的发送者与接收者之间的耦合。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它。
*
* 应用场景
* 多个对象可以处理同一请求:客户端不知道到底由哪个对象处理,这种情况下应该考虑责任链模式。
* 动态指定处理顺序:在运行时可以动态地添加新的处理者,使得顺序可变。
* 请求的发送者和接收者不需要直接互相耦合:解耦请求的发送者和接收者。
*
* Android AOSP 中的应用
* 在 Android 的 AOSP 中,责任链模式也有一些应用:
* - 事件传递和处理:Android 的事件传递机制(如 TouchEvent)采用了一种类似责任链的模式。事件可以沿着视图树传递,直到找到能够处理该事件的视图。
* - 拦截器链:在网络请求库中,比如 OkHttp,责任链模式用于实现拦截器链。每个拦截器都可以决定是否处理请求或者将其传递给下一个拦截器。
* 责任链模式通过降低请求的发送者和接收者之间的耦合度,提高了系统的灵活性和可扩展性,是一种非常实用的模式。
*
* 示例
* 假设我们有一个请求处理系统,处理不同类型的请求(例如,日志记录、用户认证、数据处理)。我们可以使用责任链模式来实现。
* 在这个示例中,Handler 类定义了处理请求的方法和设置下一个处理者的方法。
* LoggingHandler、AuthenticationHandler 和 DataHandler 是具体的处理者,它们处理特定类型的请求。
* 如果处理者不能处理请求,它会将请求传递给链中的下一个处理者。
*/
// 定义请求对象
data class Request(val type: String, val data: String)
// 抽象处理者
abstract class Handler {
protected var nextHandler: Handler? = null
fun setNext(handler: Handler): Handler {
nextHandler = handler
return handler
}
abstract fun handleRequest(request: Request)
}
// 具体处理者:日志处理
class LoggingHandler : Handler() {
override fun handleRequest(request: Request) {
if (request.type == "LOG") {
println("Logging request: ${request.data}")
} else {
nextHandler?.handleRequest(request)
}
}
}
// 具体处理者:认证处理
class AuthenticationHandler : Handler() {
override fun handleRequest(request: Request) {
if (request.type == "AUTH") {
println("Authenticating request: ${request.data}")
} else {
nextHandler?.handleRequest(request)
}
}
}
// 具体处理者:数据处理
class DataHandler : Handler() {
override fun handleRequest(request: Request) {
if (request.type == "DATA") {
println("Processing data: ${request.data}")
} else {
nextHandler?.handleRequest(request)
}
}
}
// 使用示例
@Test
fun testChainOfResponsibilityPattern() {
val loggingHandler = LoggingHandler()
val authHandler = AuthenticationHandler()
val dataHandler = DataHandler()
// 创建责任链
loggingHandler.setNext(authHandler).setNext(dataHandler)
// 发送请求
val requests = listOf(
Request("LOG", "Log this information"),
Request("AUTH", "Authenticate this user"),
Request("DATA", "Process this data"),
Request("UNKNOWN", "Unknown request")
)
for (request in requests) {
loggingHandler.handleRequest(request)
}
}
/**
* ### 3. 行为型模式
* 3.2、命令模式(Command Pattern)**:将请求封装为对象,
* 从而使你可以用不同的请求对客户进行参数化、对请求进行排队或记录请求日志,以及支持可撤销的操作。
*
* 应用场景
* 参数化请求:将请求封装为对象,并通过参数传递。
* 撤销重做功能:通过保存命令的历史记录来实现命令的撤销和重做。
* 事务日志:将请求记录到日志以支持系统崩溃后的操作重做。
* 批处理任务:将多个请求组合成一个命令对象,以便批量执行。
*
* Android AOSP 中的应用
* 在 Android 的 AOSP 中,命令模式的应用包括:
* - UI操作的封装:许多 UI 操作(如按钮点击、菜单选项选择等)可以被封装为命令对象,以便于事件的处理、撤销和重做。
* - 输入法框架:Android 的输入法框架(Input Method Framework)中,许多操作可以被抽象为命令模式,以便于输入事件的处理和调度。
* 命令模式通过将请求封装为对象,使得操作更加灵活和可扩展,同时减少了操作的耦合性,是复杂系统中有效的设计模式。
*
* 示例
* 假设我们正在开发一个遥控器应用,可以用于控制灯的开关和音量的增加,这里可以使用命令模式。
*/
// 定义命令接口
interface Command {
fun execute()
}
// 接收者类:灯
class Light {
fun on() {
println("Light is ON")
}
fun off() {
println("Light is OFF")
}
}
// 具体命令类:打开灯命令
class LightOnCommand(private val light: Light) : Command {
override fun execute() {
light.on()
}
}
// 具体命令类:关闭灯命令
class LightOffCommand(private val light: Light) : Command {
override fun execute() {
light.off()
}
}
// 接收者类:音响
class Stereo {
fun volumeUp() {
println("Stereo volume is UP")
}
fun volumeDown() {
println("Stereo volume is DOWN")
}
}
// 具体命令类:音量增加命令
class StereoVolumeUpCommand(private val stereo: Stereo) : Command {
override fun execute() {
stereo.volumeUp()
}
}
// 调用者类:遥控器
class RemoteControl {
private val commandList = mutableListOf<Command>()
fun setCommand(command: Command) {
commandList.add(command)
}
fun pressButton() {
commandList.forEach { it.execute() }
}
}
// 使用示例
@Test
fun testCommandPattern() {
// 创建接收者
val light = Light()
val stereo = Stereo()
// 创建命令
val lightOnCommand = LightOnCommand(light)
val lightOffCommand = LightOffCommand(light)
val stereoVolumeUpCommand = StereoVolumeUpCommand(stereo)
// 创建调用者
val remoteControl = RemoteControl()
// 设置命令
remoteControl.setCommand(lightOnCommand)
remoteControl.setCommand(stereoVolumeUpCommand)
remoteControl.setCommand(lightOffCommand)
// 执行命令
remoteControl.pressButton()
}
/**
* ### 3. 行为型模式
* 3.3、解释器模式(Interpreter Pattern)**:为语言创建解释器,定义语言的文法,并解析语言中的句子。
*
* 应用场景
* 语言解析:当需要实现一个简单的领域特定语言(DSL)时,可以使用解释器模式。
* 表达式求值:用于计算和处理复杂的表达式。
* 配置解析:解析特定格式的配置文件。
*
* Android AOSP 中的应用
* 在 Android 的 AOSP 中,解释器模式的应用相对较少,但在某些情况下会被间接使用:
* - 正则表达式引擎:在解析和执行正则表达式时,会使用类似解释器模式的策略,解析表达式并进行匹配操作。
* - 脚本引擎:一些 Android 应用可能会嵌入脚本引擎,解析和执行特定的脚本语言。
* 解释器模式通常用于编译器和解释器领域,它通过定义文法规则和解释器来解析和执行语言,是设计语言解析器的常用模式。虽然在日常应用开发中较少直接使用,但其思想在许多解析和计算问题中都有体现。
*
* 示例
* 我们来实现一个简单的数学表达式解析器,支持加法和减法。
*/
// 定义表达式接口
interface Expression {
fun interpret(): Int
}
// 终结符表达式:数字
class NumberExpression(private val number: Int) : Expression {
override fun interpret(): Int = number
}
// 非终结符表达式:加法
class AddExpression(private val left: Expression, private val right: Expression) : Expression {
override fun interpret(): Int = left.interpret() + right.interpret()
}
// 非终结符表达式:减法
class SubtractExpression(private val left: Expression, private val right: Expression) : Expression {
override fun interpret(): Int = left.interpret() - right.interpret()
}
// 使用示例
@Test
fun testInterpreterPattern() {
// 构建表达式:(5 - 2) + (10 - 3)
val expression = AddExpression(
SubtractExpression(NumberExpression(5), NumberExpression(2)),
SubtractExpression(NumberExpression(10), NumberExpression(3))
)
// 解释并输出结果
println("Result: ${expression.interpret()}")
}
/**
* ### 3. 行为型模式
* 3.4、迭代器模式(Iterator Pattern)**:提供一种方法访问集合对象中的各个元素,而又不需要暴露该对象的内部表示。
*
* 应用场景
* 遍历集合:当集合对象需要提供多种遍历方式时,可以使用迭代器模式。
* 隐藏内部实现:当需要对集合的遍历隐藏其内部实现细节时。
* 统一接口:需要以统一的方式遍历不同类型的集合时。
*
* Android AOSP 中的应用
* 在 Android 的 AOSP 中,迭代器模式有一些常见的应用:
* - 集合框架:Java 及 Android 的集合框架中,例如 ArrayList、HashSet 等都实现了 Iterable 接口,并提供了迭代器以供遍历。
* - 内容提供者(ContentProvider):在 Android 中,Cursor 类提供了一种迭代器的方式来遍历查询结果,虽然不是典型的迭代器模式,但其用途是类似的。
* 迭代器模式通过提供一致的遍历接口,简化了集合的遍历操作,增强了代码的灵活性和可维护性。Android 中的许多集合操作都借助迭代器来实现,可以看到其在实践中的广泛应用。
*
* 示例
* 假设我们在实现一个自定义的集合类 CustomList,并用迭代器模式为其提供遍历功能。
*/
// 定义迭代器接口
interface Iterator<T> {
fun hasNext(): Boolean
fun next(): T
}
// 定义聚合接口
interface Aggregate<T> {
fun createIterator(): Iterator<T>
}
// 具体聚合类:自定义列表
class CustomList<T>(private val items: List<T>) : Aggregate<T> {
override fun createIterator(): Iterator<T> {
return CustomListIterator(this)
}
// 获取列表长度
fun size() = items.size
// 获取指定位置的元素
fun get(index: Int): T = items[index]
}
// 具体迭代器类
class CustomListIterator<T>(private val customList: CustomList<T>) : Iterator<T> {
private var index = 0
override fun hasNext(): Boolean {
return index < customList.size()
}
override fun next(): T {
if (!hasNext()) throw NoSuchElementException()
return customList.get(index++)
}
}
// 使用示例
@Test
fun testIteratorPattern() {
val customList = CustomList(listOf("Kotlin", "Java", "Python"))
val iterator = customList.createIterator()
while (iterator.hasNext()) {
println(iterator.next())
}
}
/**
* ### 3. 行为型模式
* 3.5、中介者模式(Mediator Pattern)**:用一个中介对象封装一系列对象的交互,使对象不需要显示地互相引用,从而使其耦合松散。
*
* 应用场景
* 复杂对象交互:当多个对象紧密耦合,导致系统结构复杂时,可以使用中介者模式。
* 集中控制:需要将对象间的交互逻辑集中到一个单独的类中以便于管理和扩展时。
* 解耦对象:希望减少对象间的依赖关系,实现松耦合。
*
* Android AOSP 中的应用
* 在 Android 的 AOSP 中,中介者模式有一些应用:
* - UI 组件协调:Android 的某些复杂 UI 组件,如 FragmentManager,在管理多个 Fragment 之间的交互时,采用了类似中介者模式的概念。
* Activity 或 FragmentManager 作为中介者,协调各个 Fragment 的生命周期和事件。
* - BroadcastReceiver 和 EventBus:虽然不是典型的中介者模式,但它们也承担了类似的角色,通过广播或事件总线的方式实现组件间的解耦和消息传递。
* 中介者模式通过引入一个中介对象,简化了对象之间的复杂交互,促进了组件的独立性和代码的可维护性,这是软件设计中非常实用的模式。
*
* 示例
* 我们来实现一个简单的聊天系统,用户可以通过中介者与其他用户通信。
*/
// 定义中介者接口
interface ChatMediator {
fun sendMessage(msg: String, user: User)
fun addUser(user: User)
}
// 同事类:用户
abstract class User(protected val mediator: ChatMediator, val name: String) {
abstract fun send(msg: String)
abstract fun receive(msg: String)
}
// 具体中介者类
class ChatRoomMediator : ChatMediator {
private val users = mutableListOf<User>()
override fun addUser(user: User) {
users.add(user)
}
override fun sendMessage(msg: String, user: User) {
for (u in users) {
// 消息不发送给发送者自己
if (u != user) {
u.receive(msg)
}
}
}
}
// 具体同事类:聊天室用户
class ChatUser(mediator: ChatMediator, name: String) : User(mediator, name) {
override fun send(msg: String) {
println("$name: Sending Message = $msg")
mediator.sendMessage(msg, this)
}
override fun receive(msg: String) {
println("$name: Received Message = $msg")
}
}
// 使用示例
@Test
fun testMediatorPattern() {
val mediator = ChatRoomMediator()
val user1 = ChatUser(mediator, "Alice")
val user2 = ChatUser(mediator, "Bob")
val user3 = ChatUser(mediator, "Charlie")
mediator.addUser(user1)
mediator.addUser(user2)
mediator.addUser(user3)
user1.send("Hello, everyone!")
}
/**
* ### 3. 行为型模式
* 3.6、备忘录模式(Memento Pattern)**:在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
*
* 应用场景
* 撤销功能:需要实现撤销和恢复操作时。
* 状态快照:需要在对象状态改变前后进行比较,分析变化时。
* 历史记录:保存对象的状态历史,以便将来能够还原。
*
* Android AOSP 中的应用
* 在 Android 的 AOSP 中,备忘录模式用于以下几个地方:
* - Activity 状态恢复:当 Android 系统在旋转设备或内存不足导致 Activity 被销毁时,
* onSaveInstanceState() 和 onRestoreInstanceState() 方法用于保存和恢复 Activity 的 UI 状态。
* 这种机制与备忘录模式的思想类似,虽然实现细节不完全相同。
* - SharedPreferences:虽然不是典型的备忘录模式,但它也承担了类似的职责,通过保存和恢复应用的配置和状态信息。
* 备忘录模式的主要优点是可以在不破坏封装的情况下保存和恢复对象的状态,对于需要撤销和恢复功能的应用程序非常有用。
* Android 中对状态管理的需求很大,因此这一模式的思想经常被使用。
*
* 示例
* 我们来实现一个简单的文本编辑器,可以撤销和恢复文本的状态。
*/
// 备忘录类,存储文本状态
data class TextMemento(val text: String)
// 发起人类,负责创建和恢复备忘录
class TextEditor {
var text: String = ""
fun createMemento(): TextMemento {
return TextMemento(text)
}
fun restoreMemento(memento: TextMemento) {
text = memento.text
}
}
// 管理者类,负责保存和恢复备忘录
class Caretaker {
private val mementoList = mutableListOf<TextMemento>()
fun saveMemento(memento: TextMemento) {
mementoList.add(memento)
}
fun getMemento(index: Int): TextMemento {
return mementoList[index]
}
}
// 使用示例
@Test
fun testMementoPattern() {
val editor = TextEditor()
val caretaker = Caretaker()
// 初始状态
editor.text = "Hello"
caretaker.saveMemento(editor.createMemento())
// 修改状态
editor.text = "Hello, World"
caretaker.saveMemento(editor.createMemento())
// 再次修改状态
editor.text = "Hello, Kotlin"
println("Current Text: ${editor.text}")
// 恢复状态
editor.restoreMemento(caretaker.getMemento(0))
println("Restored Text: ${editor.text}")
}
/**
* ### 3. 行为型模式
* 3.7、观察者模式(Observer Pattern)**:定义对象间的一种一对多的依赖关系,使得当一个对象的状态发生改变时,其相关依赖对象皆得到通知并被自动更新。
*
* 应用场景
* 事件处理系统:当事件触发时,所有监听该事件的对象都会得到通知。
* 数据绑定:UI 组件可以自动更新以反映底层数据的变化。
* 发布-订阅模型:如消息队列、事件总线。
*
* Android AOSP 中的应用
* 在 Android 的 AOSP 中,观察者模式被广泛应用:
* - Observer 和 Observable 类:这些类实现了基本的观察者模式结构,一些自定义类利用这些类来通知观察者。
* - ContentObserver:用于监听内容提供者(ContentProvider)数据变化的类。
* - BroadcastReceiver:虽然不完全是观察者模式,但它也体现了类似的发布-订阅机制,应用组件可以注册接收特定的广播事件。
* - LiveData 和 ViewModel:在 Android 的 Jetpack 组件中,LiveData 基于观察者模式实现,用于观察数据变化并更新 UI。
* 观察者模式在 Android 中的广泛应用,充分体现了其在异步事件处理和数据变更通知方面的强大功能。
*
* 示例
* 我们用 Kotlin 实现一个简单的天气预报系统,多个观察者可以订阅天气变化。
*/
// 定义观察者接口
interface Observer {
fun update(temperature: Float, humidity: Float, pressure: Float)
}
// 定义主题接口
interface Subject {
fun registerObserver(observer: Observer)
fun removeObserver(observer: Observer)
fun notifyObservers()
}
// 具体主题类,保存状态并通知观察者
class WeatherData : Subject {
private val observers = mutableListOf<Observer>()
private var temperature: Float = 0.0f
private var humidity: Float = 0.0f
private var pressure: Float = 0.0f
override fun registerObserver(observer: Observer) {
observers.add(observer)
}
override fun removeObserver(observer: Observer) {
observers.remove(observer)
}
override fun notifyObservers() {
for (observer in observers) {
observer.update(temperature, humidity, pressure)
}
}
fun setMeasurements(temperature: Float, humidity: Float, pressure: Float) {
this.temperature = temperature
this.humidity = humidity
this.pressure = pressure
notifyObservers()
}
}
// 具体观察者类,实现更新接口
class CurrentConditionsDisplay(private val weatherData: Subject) : Observer {
private var temperature: Float = 0.0f
private var humidity: Float = 0.0f
init {
weatherData.registerObserver(this)
}
override fun update(temperature: Float, humidity: Float, pressure: Float) {
this.temperature = temperature
this.humidity = humidity
display()
}
private fun display() {
println("Current conditions: $temperature°F and $humidity% humidity")
}
}
// 使用示例
@Test
fun testObserverPattern() {
val weatherData = WeatherData()
val currentDisplay = CurrentConditionsDisplay(weatherData)
weatherData.setMeasurements(80.0f, 65.0f, 30.4f)
weatherData.setMeasurements(82.0f, 70.0f, 29.2f)
}
/**
* ### 3. 行为型模式
* 3.8、状态模式(State Pattern)**:允许对象在内部状态改变时改变其行为,看起来就像改变了其类。
*
* 应用场景
* 状态机:对于需要在多个状态之间切换的系统。
* 对象行为变化:对象需要在运行时根据内部状态改变其行为。
* 减少条件语句:用状态类替代条件语句来决定对象的行为。
*
* Android AOSP 中的应用
* 在 Android 的 AOSP 中,状态模式并不作为一个明确的类库存在,但思想被广泛应用,特别是在处理状态机和 UI 组件的状态切换时。以下是一些典型的应用:
* - MediaPlayer:MediaPlayer 的内部状态机使用了状态模式的思想来管理播放、暂停、停止等多种状态。
* - Activity 生命周期:虽然不是直接实现状态模式,但 Activity 的生命周期方法(如 onCreate、onStart、onResume、onPause 等)实际上体现了不同状态下的行为变化。
* - 动画系统:Android 的动画系统中常常需要根据状态来执行不同的动画逻辑。
* 总之,状态模式是一种处理对象状态相关行为变化的强大工具,在 Android 中的应用主要体现在需要动态改变对象行为的场景。
*
* 示例
* 我们用 Kotlin 实现一个简单的电灯状态系统,电灯有两种状态:开和关。
*/
// 状态接口
interface State {
fun handle(context: LightContext)
}
// 具体状态:开灯状态
class LightOnState : State {
override fun handle(context: LightContext) {
println("Light is already ON. Switching to OFF.")
context.state = LightOffState()
}
}
// 具体状态:关灯状态
class LightOffState : State {
override fun handle(context: LightContext) {
println("Light is OFF. Switching to ON.")
context.state = LightOnState()
}
}
// 上下文类:维护当前状态
class LightContext {
var state: State = LightOffState() // 默认状态
fun request() {
state.handle(this)
}
}
// 使用示例
@Test
fun testStatePattern() {
val light = LightContext()
// 切换状态
light.request() // Light is OFF. Switching to ON.
light.request() // Light is already ON. Switching to OFF.
light.request() // Light is OFF. Switching to ON.
}
/**
* ### 3. 行为型模式
* 3.9、策略模式(Strategy Pattern)**:定义一系列算法,把它们一个个封装起来,并且它们可以相互替换。
*
* 应用场景
* 算法变体:需要使用不同的算法解决同一问题时。
* 运行时策略切换:需要在运行时切换算法或者策略。
* 避免条件语句:用策略替代条件语句来决定行为。
*
* Android AOSP 中的应用
* 在 Android 的 AOSP 中,策略模式的思想被广泛应用,特别是在需要不同算法或行为的组件中。以下是几个典型的应用:
* - InputMethod 框架:不同的输入法(软键盘)可以看作是不同的策略,系统根据用户选择动态设置当前输入法。
* - View 的绘制策略:在自定义视图中,可以通过选择不同的 Canvas 绘制策略来实现不同的视觉效果。
* - ListView 的适配器模式:虽然更接近于适配器模式,但 ListAdapter 或 RecyclerView.Adapter 可以动态定义视图的数据绑定和显示策略。
* 策略模式允许 Android 组件在不修改代码的情况下切换不同的行为,这在需要灵活性和可扩展性时特别有用。
*
* 示例
* 我们用 Kotlin 实现一个简单的支付系统,不同的支付方式是不同的策略。
*/
// 策略接口
interface PaymentStrategy {
fun pay(amount: Double)
}
// 具体策略:信用卡支付
class CreditCardPayment(private val cardNumber: String, private val cardHolderName: String) : PaymentStrategy {
override fun pay(amount: Double) {
println("Paid $$amount using Credit Card: $cardNumber")
}
}
// 具体策略:PayPal支付
class PayPalPayment(private val email: String) : PaymentStrategy {
override fun pay(amount: Double) {
println("Paid $$amount using PayPal: $email")
}
}
// 上下文:结账
class ShoppingCart {
private var paymentStrategy: PaymentStrategy? = null
fun setPaymentStrategy(strategy: PaymentStrategy) {
this.paymentStrategy = strategy
}
fun checkout(amount: Double) {
paymentStrategy?.pay(amount) ?: throw IllegalStateException("Payment strategy not set")
}
}
// 使用示例
@Test
fun testStrategyPattern() {
val cart = ShoppingCart()
// 使用信用卡支付
cart.setPaymentStrategy(CreditCardPayment("1234-5678-9876-5432", "John Doe"))
cart.checkout(100.0)
// 使用PayPal支付
cart.setPaymentStrategy(PayPalPayment("john.doe@example.com"))
cart.checkout(200.0)
}
/**
* ### 3. 行为型模式
* 3.10、模板方法模式(Template Method Pattern)**:在一个方法中定义一个算法的骨架,而将一些步骤推迟到子类中。
*
* 应用场景
* 算法骨架:需要定义一个算法的不可变部分并允许子类实现其可变部分。
* 代码复用:多个类有相同的算法结构,但在某些步骤上有所不同。
* 控制流程:允许子类在不改变算法的情况下改变算法的某些步骤。
*
* Android AOSP 中的应用
* 在 Android AOSP 中,模板方法模式的思想广泛应用于框架设计中,以下是一些典型的应用:
* - Activity 生命周期方法:Activity 类提供了一系列生命周期方法(如 onCreate(), onStart(), onResume() 等),这些方法的调用顺序由系统控制,而具体实现由开发者提供。系统在生命周期的不同阶段调用这些方法,为开发者提供了在不同阶段插入代码的钩子。
* - View 类:View 类中的 onDraw(), onMeasure(), onLayout() 等方法,定义了视图绘制和布局的基本流程,不同的视图类通过实现这些方法来自定义其行为。
* - AsyncTask:AsyncTask 类中的 doInBackground(), onPreExecute(), onPostExecute() 等方法,定义了异步任务的执行流程,不同任务通过实现这些方法来定义具体的异步逻辑。
* 模板方法模式在 AOSP 中的广泛应用,使得开发者可以在不改变框架核心功能的基础上,自定义和扩展应用程序的行为。
*
* 示例
* 我们用 Kotlin 实现一个饮品制作系统,其中制作饮品(如茶和咖啡)是模板方法,不同的饮品有不同的制作步骤。
*/
// 抽象类定义了算法骨架
abstract class CaffeineBeverage {
// 模板方法,定义了算法的结构
fun prepareRecipe() {
boilWater()
brew()
pourInCup()
if (customerWantsCondiments()) {
addCondiments()
}
}
// 具体方法
private fun boilWater() {
println("Boiling water")
}
// 具体方法
private fun pourInCup() {
println("Pouring into cup")
}
// 抽象方法,由子类实现
protected abstract fun brew()
protected abstract fun addCondiments()
// 钩子方法,子类可选择重写
open fun customerWantsCondiments(): Boolean {
return true
}
}
// 具体类:茶
class Tea : CaffeineBeverage() {
override fun brew() {
println("Steeping the tea")
}
override fun addCondiments() {
println("Adding lemon")
}
}
// 具体类:咖啡
class Coffee : CaffeineBeverage() {
override fun brew() {
println("Dripping Coffee through filter")
}
override fun addCondiments() {
println("Adding sugar and milk")
}
}
// 使用示例
@Test
fun testTemplateMethodPattern() {
val tea = Tea()
println("Making tea...")
tea.prepareRecipe()
val coffee = Coffee()
println("\nMaking coffee...")
coffee.prepareRecipe()
hashCode()
}
/**
* ### 3. 行为型模式
* 3.11、访问者模式(Visitor Pattern)**:表示一个作用于某对象结构中的各元素的操作,可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
*
* 应用场景
* 处理复杂对象结构:当一个对象结构包含许多不同种类的对象,并且需要对这些对象执行不同的操作时。
* 需要增加新操作:希望在不改变对象结构的前提下增加新的操作。
* 对象结构稳定:当对象结构很少改变,但需要经常添加新操作时。
*
* Android AOSP 中的应用
* 在 Android 的 AOSP 中,访问者模式并不常作为一个明确的设计模式被使用,因为其通常适用于具有复杂结构的系统,Android 内部的系统设计更多使用其他模式。
* 然而,访问者模式的思想在某些场景中也有体现:
* - 编译器和解释器:虽然不在 Android 主项目中,但在处理语法树和编译器的设计中,访问者模式是常用的设计模式。它们通过访问者来遍历和处理不同类型的语法节点。
* - 数据处理框架:某些自定义的数据处理框架可能使用访问者模式来实现对不同数据类型的操作。
* 总体来说,访问者模式在 Android 中的使用相对不如其他模式普遍,因为它通常适用于需要扩展操作而不是修改对象结构的情况,而 Android 更倾向于使用组件化和接口驱动的设计。
*
* 示例
* 我们用 Kotlin 实现一个简单的文件系统结构,其中不同类型的文件节点(如文件和文件夹)是元素,访问者可以对这些节点执行不同的操作(如计算大小或显示名称)。
*/
// 访问者接口
interface FileSystemVisitor {
fun visitFile(file: File)
fun visitDirectory(directory: Directory)
}
// 元素接口
interface FileSystemElement {
fun accept(visitor: FileSystemVisitor)
}
// 具体元素:文件
class File(val name: String, val size: Int) : FileSystemElement {
override fun accept(visitor: FileSystemVisitor) {
visitor.visitFile(this)
}
}
// 具体元素:目录
class Directory(val name: String, val elements: List<FileSystemElement>) : FileSystemElement {
override fun accept(visitor: FileSystemVisitor) {
visitor.visitDirectory(this)
}
}
// 具体访问者:计算总大小
class SizeCalculatorVisitor : FileSystemVisitor {
var totalSize: Int = 0
override fun visitFile(file: File) {
totalSize += file.size
}
override fun visitDirectory(directory: Directory) {
for (element in directory.elements) {
element.accept(this)
}
}
}
// 具体访问者:显示文件结构
class NamePrinterVisitor : FileSystemVisitor {
private val indent = " "
private var level = 0
override fun visitFile(file: File) {
println("${indent.repeat(level)}File: ${file.name}")
}
override fun visitDirectory(directory: Directory) {
println("${indent.repeat(level)}Directory: ${directory.name}")
level++
for (element in directory.elements) {
element.accept(this)
}
level--
}
}
// 使用示例
@Test
fun testVisitorPattern() {
val root = Directory("root", listOf(
File("file1.txt", 10),
Directory("subdir", listOf(
File("file2.txt", 20)
))
))
val sizeVisitor = SizeCalculatorVisitor()
root.accept(sizeVisitor)
println("Total size: ${sizeVisitor.totalSize} bytes")
val nameVisitor = NamePrinterVisitor()
root.accept(nameVisitor)
}
}