常用设计模式
六大原则–核心是降低类间的耦合
单一职责原则
一个类只负责一个变化
开放封闭原则
开发是指扩展是开发的,修改是封闭。
里氏代换原则
使用的基类的地方可以都可以被其子类,完美的替换基类。
要求:子类的方法都必须在父类中进行声明。( 用子类来代替父类,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。)
实际上:父类一般是抽象或者接口,子类是实现类
依赖倒置原则
高低层模块不相互依赖,两者都依赖抽象;细节应该依赖抽象
高层模块是指调用者,低层模块是指具体实现类
在java中的表现:模块间是通过抽象类或者接口来实现的。
迪米特法则(最少知道原则)
一个对象要尽可能减少对其他对象的了解
实现:两个对象通过第三方来调用对方的方法
接口隔离原则
使用多个隔离的接口比使用有大量方法的接口好
创建型设计模式
单例模式
定义:保证一个类只有一个实例,并且提供一个访问该全局的访问点
(DCL)
java版
class Bank{
private Bank(){}
private volatile static Bank instance = null;
public static Bank getInstance(){
if (instance == null) {//立牌坊通知
synchronized (Bank.class) {
if (instance == null) {
instance = new Bank();
}
}
}
return instance;
}
kotlin版
class MediaPlayerHelp private constructor(context: Context){
val mContext=context
init {
val mMediaPlayer= MediaPlayer()
} //构造器执行的构造函数
companion object{
@Volatile private var instance:MediaPlayerHelp? =null
fun getInstance(context: Context){
instance?: synchronized(this){
instance?: MediaPlayerHelp(context).also { instance=it }
}
}
}
}
(静态内部类单例模式)
第一次加载Singleton类的时候不会初始化sinstance.因为内部类对于外部类来说也是一个字段,字段默认为null。只有在第一次调用getInstance时
虚拟机才会加载SingletonHolder,并初始化Sinstance.
public class Singleton {
private Singleton(){}
public static Singleton getInstance(){
return SingletonHolder.sInstance;
}
private static class SingletonHolder{
private static final Singleton sInstance=new Singleton();
}
}
(枚举单例)
使用枚举实现单例模式,由jvm从根本上提供保障单例。
那些地方用到了单例模式
网站的计数器,应用程序的日志应用,Windows的(任务管理器)windows的(回收站)。
工厂模式
参考链接
定义:我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象
简单工厂模式:在工厂中就定义了可实例化的类型,如果要添加新的类型需要修改代码

工厂直接生产产品
工厂方法模式:最顶层的工厂里只有抽象方法,生成具体产品的任务分发给具体的产品工厂

concreteFactory1中的CreateProduct()的返回对象是AbstractProduct的实现子类
建造者模式
定义:将一个复杂对象的构建过程与表示分离开来,使得同样的构建过程可以创建不同的表示

Director为指挥者/导演类,负责安排已有模块的顺序,然后告诉Builder开始建造;
Dircector类里会有一个Bulider的字段,来安排Builder里的产品顺序。
Builder是抽象建造者,规范产品的组建,一般由子类实现;
ConcreteBuilder是具体建造者,实现抽象类定义的所有方法,并且返回一个组建好的对象;
Product是产品类,通常实现了模板方法模式。
在实际中,Dircector构造器中传入一个具体的建设者,然后传入的建设者会按照director的安装方法来调用自己的组件,最后会返回一个产品
Director director = new Director();
director.Construct(builder1);
Product product1 = builder1.getResult();
优点:客户端不必知道产品内部组成的细节

结构性设计模式
代理模式
定义:给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。

抽象主题角色(Subject):声明了目标对象和代理对象的共同接口,
具体主题角色(RealSubject):也称为被代理角色。定义了代理对象所代表的目标对象。
代理主题角色(Proxy):也叫代理类。代理对象内部含有目标对象的引用,从而可以在任何时候操作目标对象;在其所实现的接口中调用RealSubject中的相应方法
动态代理:通过反射动态生成代理类
客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对
象。
问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。
(通过Proxy.newProxyInstance()实现)
问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。
(通过InvocationHandler接口的实现类及其方法invoke())
静态代理:在程序运行前,代理类的.class文件就已经存在了
2.2 静态代理的缺点:
① 代理类和目标对象的类都是 在编译期间确定下来,不利于程序的扩展。
② 每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。
装饰模式
定义:以对客户透明的方式动态地给一个对象附加上更多的责任。换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不使用创造更多子类的情况下,将对象的功能加以扩展。

装饰(Decorator)角色:持一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
在具体装饰上,会定义一些额外的功能,然后再抽象接口的实现函数中进行调用
ConcreteDecoratorA的方法

super.sampleOperation()是指Dector的方法,里面实现是组件的sampleOperation()方法。
然后具体装饰对象调用接口方法,比原先的具体组件调用接口方法实现的功能更多一些。
外观模式
定义:要求一个子系统的外部与内部的通信要通过一个统一的对象来进行

facade类中会有对各个子系统的引用,facade中的每一个方式都是调用子系统中相应的方法进行组合产生的
优点:减少了系统的相互依赖程度,所有的依赖都是对外观类的依赖(子系统的应用都是private,外观类的方法也是对子系统方法的引用)
加强了安全性,客户无法修改子系统的方法。
享元模式
定义:以共享的方式高效的支持大量的细粒度对象。
好处:减少应用程序创建的对象,降低程序内存的占用,提高程序的性能
享元对象区分内蕴状态(Internal State)和外蕴状态(External)。
一个内蕴状态是存储在享元对象内部的,并且是不会随环境改变而有所不同的。(作为创建对象的依据,一般作为map的key)
一个外蕴状态是随环境改变而改变的、不可以共享的状态。(可以根据不同的环境来创建对象的不同外蕴状态,(switch,case)
工厂类一般情况有一个map字段,用来存储不同内蕴状态的享元对象

如果一个享元对象有外蕴状态的话,所有的外部状态都必须存储在客户端,在使用享元对象时,再由客户端传入享元对象。
享元工厂角色类,必须指出的是,客户端不可以直接将具体享元类实例化,而必须通过一个工厂对象,利用一个factory()方法得到享元对象。
public class FlyweightFactory {
2
3 //定义一个池容器
4 private static HashMap<String, Flyweight> pool = new HashMap<>();
5
6 //享元工厂
7 public static Flyweight getFlyweight(String extrinsic) {
8 Flyweight flyweight = null;
9
10 if(pool.containsKey(extrinsic)) { //池中有该对象
11 flyweight = pool.get(extrinsic);
12 System.out.print("已有 " + extrinsic + " 直接从池中取---->");
13 } else {
14 //根据外部状态创建享元对象
15 flyweight = new ConcreteFlyweight(extrinsic);
16 //放入池中
17 pool.put(extrinsic, flyweight);
18 System.out.print("创建 " + extrinsic + " 并从池中取出---->");
19 }
20
21 return flyweight;
22 }
23 }
当客户端需要单纯享元对象的时候,需要调用享元工厂的factory()方法,并传入所需的单纯享元对象的内蕴状态,由工厂方法产生所需要的享元对象。之后再由客户端对生产出来的对象进行外部状态的传入
Flyweight flyweightX = FlyweightFactory.getFlyweight("X");
7 flyweightX.operate(++ extrinsic);
8
9 Flyweight flyweightY = FlyweightFactory.getFlyweight("Y");
10 flyweightY.operate(++ extrinsic);
使用场景:
需要大量相似的对象
需要缓存池
行为型设计模式
策略模式
定义:使算法定义独立于它的客户之外

环境类(Context)::该类的构造器中包含了 策略类,通过传入不同的具体策略来产生不同的效果。
public class Context {
//持有一个具体策略的对象
private Strategy strategy;
public Context(Strategy strategy){
this.strategy = strategy;
}
/**
* 策略方法
*/
public void contextInterface(){
strategy.strategyInterface();
}
抽象策略类(Strategy):定义所有支持的算法的公共接口。 Context使用这个接口来调用某ConcreteStrategy定义的算法。
具体策略类(ConcreteStrategy):以Strategy接口实现某具体算法。
环境类可以根据自足选择具体的策略类,减少if语句
与工厂模式的区别
我们去旅行。策略模式的做法:有几种方案供你选择旅行,选择火车好呢还是骑自行车,完全有客户自行决定去构建旅行方案(比如你自己需要去买火车票,或者机票)。而工厂模式是你决定哪种旅行方案后,不用关注这旅行方案怎么给你创建,也就是说你告诉我方案的名称就可以了,然后由工厂代替你去构建具体方案(工厂代替你去买火车票)。
缺点:
上层模块需要知道有哪些策略,才能使用这些策略,这违背了迪米特原则
模板方法模式
AbstractClass叫做抽象模板,它的方法分为两类:基本方法(由子类去实现)和模板方法(可以有一个或多个,也就是一个框架,实现对基本方法的调度,完成固定的逻辑)。
为了防止恶意的操作,一般模板方法上都添加上final关键字,不允许被覆写。


ps.可以提取子类的公共部分代码放入抽象类作为一个普通方法,便于维护
观察者模式
定义:对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
关键:(具体被观察者)类中有一个 ArrayList 存放观察者们

Observer类是抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己

本文探讨了软件设计的六大原则,聚焦于降低类间耦合,涵盖单例模式、工厂模式、建造者模式等创建型模式,以及策略模式、模板方法模式和观察者模式的行为型设计。实例解析了每种模式的应用场景和实现方法,帮助理解如何在实际项目中高效利用这些设计思想。
1116

被折叠的 条评论
为什么被折叠?



