Java常用设计模式
设计模式是对某一类问题的最优解决方案,熟练使用设计模式可以有效提高我们的代码质量和性能以及节省我们开发迭代过程中的工作量。
Java中通常我们人为有23种设计模式,以下将列举在开发中比较实用的几种进行讲解和分析。
单例模式
-
使用场景
当需要某个类仅存在一个实例
-
优点
更加方便快捷的访问方式,节省不必要的性能开销,便于管理。
-
思路
要保证仅存在一个实例就需要隐藏掉自身本来的构造方法,通常采用将构造方法私有化(private)来实现,再通过一个静态方法来得到唯一实例,而实例是否唯一就需要在方法中自行编辑逻辑进行判断。
-
懒汉式
class SingleInstance private constructor() { companion object { private var instance: SingleInstance? = null fun getInstance(): SingleInstance { if (instance == null) { instance = SingleInstance() } return instance!! } } }
线程安全:双重检查锁,相比传统同步方法提高性能。
class SingleInstance private constructor() { companion object { @Volatile private var instance: SingleInstance? = null fun getInstance(): SingleInstance { if (instance == null) { synchronized(SingleInstance ::class.java) { if (instance == null) { instance = SingleInstance() } } } return instance!! } } }
-
饿汉式
class SingleInstance private constructor() {
companion object {
private var instance = SingleInstance()
fun getInstance(): SingleInstance {
return instance
}
}
}
- object
kotlin 原生的实现,通过查看编译后的字节码反编译为Java可以发现,实现方式为饿汉式单例
object SingleInstance{}
初始化时机 | 优点 | 缺点 | |
---|---|---|---|
饿汉式 | 类加载时 | 使用简单、初始化效率高 | 容易造成类加载卡顿 |
懒汉式 | 实际使用时 | 精确控制初始化时机,提高启动速度 | 使用复杂、需要考虑并发问题 |
工厂模式
-
使用场景
需要得到类的实例但不希望与该类形成耦合
需要得到类的实例但不知道该类具体有哪些子类 -
优点
与特定类解耦,不必关心所需对象的创建过程。
-
思路
其主要目的就是为了规避类的具体构造过程实现解耦的目的,实现方式有非常多种,可以不一定按照标准的写法进行设计,根据实际的需求,在节约开发成本的前提下有理即可。
-
示例
简单工厂模式(静态工厂模式):缺点结构固定,如要添加新产品需要重新编写创造代码,违背开闭原则。
class Car(val name: String) { fun getName() { System.out.print("name:$name") } } object Factory { fun createCar(name:String)=when(name){ "Benz"-> Car(name) "BMW"-> Car(name) else-> throw RuntimeException("") } }
工厂方法模式:即针对每个产品创建多个工厂对象,缺点整合性不高,会伴随多个工厂类。
object BenzFactory { fun create() = Car("Benz") } object BmwFactory { fun create() = Car("BMW") }
抽象工厂模式:工厂方法模式的升级版,
internal interface IFactory { fun create(): Car } object BenzFactory : IFactory { override fun create() = Car("Benz") } object BmwFactory : IFactory { override fun create() = Car("BMW") }
建造者模式(Builder Pattern)
-
使用场景
需要创建一个构造复杂的对象时
-
优点
创建的流程结构清晰
根据不同的生成器生成特定属性的对象
使用者无需了解对象的具体组件
如工厂模式一般,将生成对象进行解耦
满足开闭合原理,便于增加新的生成条件 -
思路
建造者模式在我的理解里就是一种让代码结构更加工整的工厂模式。在Android中最常见的实现有官方的
AlertDialog
,其构建的一般思路如下:- 以静态内部类的形式存在于所需生成类中
- 持有外部类的实例的引用
- 编写需要修改实例参数的对应方法
- 每个配置方法以Builder类本身作为返回值
-
示例
class Card { fun setColor() { } fun setSize() { } companion object { class CardBuilder { private val card = Card() fun setColor(): CardBuilder { card.setColor() return this } fun setSize(): CardBuilder { card.setSize() return this } fun build() = card } } }
原型模式
-
使用场景
通过一个已经创建的对象获得与其属性相同的一个或更多对象
-
优点
当创建过程复杂时,可以提高效率
保存当前对象的状态,类似于复制品,可以作为记录保存 -
思路
实现一个可以“克隆”自身必要属性的接口,通过该接口生成一个和当前属性完全一样的新实例。Java中默认已经实现了该接口,即
Object.clone()
方法。 -
示例
interface Cloneable<T> { fun clone(): T } class Person(var sex: Int) : Cloneable<Person> { override fun clone(): Person { return Person(sex) } }
责任链模式
-
使用场景
一个请求可以被多个对象处理,希望程序在运行期间由多个对象串联消化该请求的处理
-
优点
低耦合 可以任意指定处理者的先后顺序 调用简单,不需要编写额外的判断逻辑
-
思路
定义通用接口,接口中至少定义两个方法,方法一用于获得下个执行者的实例,方法二为具体执行逻辑。
-
示例
interface Handler { fun handleRequest(n: Int): String fun setNextHandler(next: Handler) } class Handler1 : Handler { private var nextHandler: Handler? = null override fun handleRequest(n: Int): String { if (n < 0) { return "Handler1" } else { nextHandler?.let { return it.handleRequest(n) } } throw NullPointerException("") } override fun setNextHandler(next: Handler) { nextHandler = next } } class Handler2 : Handler { private var nextHandler: Handler? = null override fun handleRequest(n: Int): String { if (n in 1..10) { return "Handler2" } else { nextHandler?.let { return it.handleRequest(n) } } throw NullPointerException("") } override fun setNextHandler(next: Handler) { nextHandler = next } } class Handler3 : Handler { private var nextHandler: Handler? = null override fun handleRequest(n: Int): String { if (n in 11..100) { return "Handler3" } else { nextHandler?.let { return it.handleRequest(n) } } throw NullPointerException("") } override fun setNextHandler(next: Handler) { nextHandler = next } } fun main() { val handler1 = Handler1() val handler2 = Handler2() val handler3 = Handler3() handler1.setNextHandler(handler2) handler2.setNextHandler(handler3) handler1.handleRequest(-1) handler1.handleRequest(1) handler1.handleRequest(11) }
中介者模式
-
使用场景
多个对象之间需要进行数据交换
-
优点
低耦合,调用者不需要关心对象之间具体的交互逻辑 对象之间不用直接关联,逻辑清晰
-
思路
中介者和交互对象需要互相持有对方的实例,交互对象中调用交互方法委托中介者去告知其他交互对象。
-
示例
//Mediator(抽象中介者) interface Mediator { fun contact(message: String, colleague: Colleague) } //Colleague(抽象同事类) abstract class Colleague(protected var name: String, protected var mediator: Mediator) { abstract fun sendMessage(message: String) abstract fun getMessage(message: String) } //ConcreteColleague(具体同事类) class ConcreteColleagueHR(name: String, mediator: Mediator) : Colleague(name, mediator) { override fun sendMessage(message: String) { mediator.contact(message, this) } override fun getMessage(message: String) { println("HR#$name#:$message") } } class ConcreteColleagueAndroidDeveloper(name: String, mediator: Mediator) : Colleague(name, mediator) { override fun sendMessage(message: String) { mediator.contact(message, this) } override fun getMessage(message: String) { println("Android Developer#$name#:$message") } } //ConcreteMediator(具体中介者) class ConcreteMediator : Mediator { var concreteColleagueHR: ConcreteColleagueHR? = null var concreteColleagueAndroidDeveloper: ConcreteColleagueAndroidDeveloper? = null override fun contact(message: String, colleague: Colleague) { if (colleague == concreteColleagueHR) { concreteColleagueAndroidDeveloper?.getMessage(message) } else { concreteColleagueHR?.getMessage(message) } } } fun main() { val mediator = ConcreteMediator() val hr = ConcreteColleagueHR("Google招聘专员", mediator) val ad = ConcreteColleagueAndroidDeveloper("屌丝开发者", mediator) mediator.concreteColleagueHR = hr mediator.concreteColleagueAndroidDeveloper = ad hr.sendMessage("Hi,你有意向来我们公司吗?") ad.sendMessage("是Google开发Android吗?") hr.sendMessage("yes!") ad.sendMessage("我愿意!") }
装饰者模式
-
使用场景
在不改变已有类属性的前提下,动态地将责任追加到对象之上.
-
优点
拓展简单,不需要对已经存在的类进行修改
健壮性强,适用面很广
-
思路
简单来说就是通过对已有对象的二次封装来拓展新的能力
JDK中实现:IO Stream框架。
-
示例
很简单就不额外写了,详情可看Stream源码。
观察者模式
-
使用场景
建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应作出反应。
-
优点
一个被观察者可以对应多个观察者,而且这些观察者之间可以没有任何相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展。
减少对象之间的耦合有利于系统的复用,但是同时设计师需要使这些低耦合度的对象之间能够维持行动的协调一致,保证高度的协作。
-
思路
观察者的宗旨就是建立单对象与多对象之间的依赖关系,多对象彼此之间不耦合,每次只需操作被观察者对象,由它来通知其他所有观察者对象。
其实从生活中来看,就像一个我们的聊天群,群主是被观察者,群成员是观察者,我们需要向该群宣布一些事务,不必挨个去通知,只需要告知群主,由群主自行管理和通知其群内所有成员。
-
示例
//抽象观察者 interface Observer { void update(String state); } //具体观察者 class ProgramMonkeyObserver implements Observer { @Override public void update(String state) { System.out.println("Programer look the SDK download process is: "+state); } } //抽象被观察者 abstract class Subject { private List<Observer> list = new ArrayList<>(); public void attach(Observer observer) { list.add(observer); } public void detach(Observer observer) { list.remove(observer); } public void motifyObservers(String newState) { for (Observer observer : list) { observer.update(newState); } } } //具体被观察者 class SDKDownloadSubject extends Subject { public void netProcessChange(String data) { this.motifyObservers(data); } }
模板模式
-
使用场景
已知一套算法流程,具体步骤实现不同,但是大致的执行顺序相同,为了避免多次重新编写逻辑,定义一个算法骨架,每次只需要实现具体每步步骤即可。
-
优点
节省工作量,提高代码复用能力
便于维护和梳理业务逻辑
-
思路
模板模式其实但凡对代码质量有一定要求的人都肯定有用过,只是不知道这已经被前人归纳成为了设计模式而已。
最常见使用方式是在业务类中,定义一个抽象基类,该类中定义好业务所需的所有步骤的方法,可不实现,最后在一个方法中将所有定义好的步骤按照需要的流程顺序进行调用。
-
示例
以程序猿的一天为例( ⊙ o ⊙ ):
abstract class Monkey{ abstract fun getUp() abstract fun eat() abstract fun goToWork() abstract fun coding() abstract fun goToHome() abstract fun sleep() fun datStart() { getUp() eat() goToWork() coding() eat() coding() goToHome() eat() sleep() } }
-