• ==欢迎来访我的个人博客== :Mosey

什么是设计模式

  • 通俗来讲,设计模式是一些前人,一些大佬们从一次次的失败中总结出来的一种解决问题的方案。
  • java中的23种设计模式简单的脑图

设计模式的几大原则

  • 开闭原则:对扩展开放,对修改关闭。(即可新增代码,但不要修改原有的代码。)
  • 里氏代换原则:继承的时候,子类可以扩展父类或者超类的功能,但是不要改父类的方法和功能等等。
  • 依赖倒置原则:指面向接口编程,而不是面向实现编程
  • 单一职责原则:一个方法或者一个类,尽量只负责做一个职责。
  • 接口隔离原则:为系统中各个类建立它们对应的专用接口,每一个接口应该承担一种相对独立的角色,接口要做的事它就做,不要做的它就不做。
  • 迪米特法则(最少知道原则):只与你的直接朋友交谈,不和”陌生人“交谈(A->B->C,其中,A的直接朋友是B,B的直接朋友是C,A和C可理解为陌生人,此时如果A要向C通信拿数据,则可以通过B来操作,B可理解为中间件)
  • 合成复用原则

常见设计模式

单例模式

只能有一个自己的对象实例,所以叫单例。

  • 懒汉式(没加synchronized所以线程不安全,因为它很懒,所以需要时才创建对象)
public class Lazy {

private static Lazy lazy;
//这里使用私有的构造方法,而不用public(不能被实例化)
private Lazy() {
}

public static Lazy getInstance() {

if (lazy == null) {
return new Lazy();
}
return lazy;
}
}
  • 饿汉式(线程安全,因为它很饿,所以虚拟机启动后就马上创建对象,不管你要不要)
public class Hungry {

private static Hungry hungry = new Hungry();
//这里使用私有的构造方法,而不用public(不能被实例化)
private Hungry() {
}

public static Hungry getInstance() {
return hungry;
}
}

代理模式

现实生活中,每个地方,乡镇等火车票代售点就是官方售票的代理。我们可以不用去到很远的火车站购票,直接在家附近的代售点就可以买票回家了,方便快捷。
代理分为静态代理和动态代理,静态代理又包括jdk动态代理和cglib动态代理。

静态代理

购票接口

  • 购票方法
public interface ITicket {

void ticket();

}

购票接口实现类

  • 我要购票
public class Station implements ITicket {

@Override
public void ticket() {
System.out.println("买一张去广州的火车票");
}
}

火车站代理

  • 我帮你代理购票
public class StationProxy implements ITicket {

private Station station;

//公有的 Station 代理构造方法,拿到 Station 订票的权利
public StationProxy(Station station) {

this.station = station;
System.out.println("我是代理点,我拿到了 Station 订票的权利,可以给你订票");

}

@Override
public void ticket() {

station.ticket();
System.out.println("购票成功");

}

}

测试

  • 购票结果
Station station = new Station();
StationProxy stationProxy = new StationProxy(station);
stationProxy.ticket();

结果

jdk动态代理
购票接口

public interface ITicket {

void ticket(String str);

}

购票接口实现类

public class Station implements ITicket {

@Override
public void ticket(String str) {
System.out.println("买一张去广州的火车票");
}
}

动态代理类

//实现InvocationHandler接口,重写invoke方法
public class Dynamic implements InvocationHandler {

private Object object;

public Object creatProxy(Object object) {
//将目标对象传入进行代理
this.object = object;
//将代理对象返回 //其中有三个参数
return Proxy.newProxyInstance(
object.getClass().getClassLoader(),
object.getClass().getInterfaces(),
this
);
}


@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//调用前
before();

Object invoke = method.invoke(object, args);
//调用后
after();
return invoke;
}

private void before() {
System.out.println("调用前......");
}

private void after() {
System.out.println("调用后");
}
}

测试

Dynamic dynamic = new Dynamic();
ITicket proxy = (ITicket)dynamic.creatProxy(new Station());
proxy.ticket("par");

结果:

工厂模式

  • 简单工厂模式

描述花的接口

public interface IFlower {
String describe();
}

玫瑰花类

public class Rose implements IFlower{
@Override
public String describe() {
System.out.println("我是玫瑰花");
return "我是玫瑰";
}
}

百合花类

public class Lily implements IFlower {
@Override
public String describe() {
System.out.println("我是百合花");
return "我是百合";
}
}

工厂类一

public class FlowerFactory {

public static Rose creatRose(){
Rose rose = new Rose();
return rose;
}

public static Lily creatLily(){
Lily lily = new Lily();
return lily;
}
}

测试

Lily lily = FlowerFactory.creatLily();
String lilyDesc = lily.describe();
Rose rose = FlowerFactory.creatRose();
String roseDesc = rose.describe();

第二种工厂类方法,传入一个对应的花名的参数,在工厂类中作判断,然后生产出对应的花。
工厂类二

//传入一个参数,工厂制造一个对应的产品 
public static IFlower create(String name){
if("lily".equals(name)){
return new Lily();
}else if("rose".equals(name)){
return new Rose();
}
return null;
}

测试

IFlower rose = FlowerFactory.create("rose");
String describe = rose.describe();
  • 工厂方法模式(日常开发中常用)
    前面的简单工厂模式,所有的产品都是在同一个工厂生产的,不是很友好 ,我们现在用工厂模式,分别为每个品种开一个工厂。

玫瑰工厂

public class RoseFactory {
public Rose create(){
return new Rose();
}
}

百合工厂

public class LilyFactory {
public Lily create(){
return new Lily();
}
}

测试

RoseFactory roseFactory = new RoseFactory();
Rose rose = roseFactory.create();
String roseDesc = rose.describe();

LilyFactory lilyFactory = new LilyFactory();
Lily lily = lilyFactory.create();
String lilyDesc = lily.describe();
  • 抽象工厂模式
    适用于横向增加同类工厂,不适合新增功能这样的纵向扩展。
    总工厂接口
public interface IFactory {
IFlower instance();
}

玫瑰工厂

//玫瑰工厂实现总工厂接口
public class RoseFactory implements IFactory {
@Override
public IFlower instance(){
return new Rose();
}
}

百合工厂

//百合工厂实现总工厂接口
public class LilyFactory implements IFactory {
@Override
public IFlower instance() {
return new Lily();
}
}

测试

IFactory roseFactory = new RoseFactory();
IFlower rose = roseFactory.instance();
String roseDesc = rose.describe();

IFactory lilyFactory = new LilyFactory();
IFlower lily = lilyFactory.instance();
String lilyDesc = lily.describe();

策略模式

适用于策略少于等于4个的时候,太多策略会造成策略膨胀。

数学加减法接口

public interface IMath {

public int addOrSub(int a, int b);

}

加法类

//加法类实现IMath接口
public class Add implements IMath {

@Override
public int addOrSub(int a, int b) {

return a + b;
}
}

减法类

//减法类实现IMath接口
public class Sub implements IMath {

@Override
public int addOrSub(int a, int b) {

return a - b;
}
}

计算类

public class Calculated {

private IMath iMath;

public Calculated(IMath iMath) {

this.iMath = iMath;

}

//开始计算
public int startCal(int a, int b) {

return iMath.addOrSub(a, b);
}

}

测试

//构造计算类时候,通过传递的参数确定是加法还是减法
Calculated addCal = new Calculated(new Add());
int add = addCal.startCal(3, 2);

Calculated subCal = new Calculated(new Sub());
int sub = subCal.startCal(1, 10);

观察者模式

观察者模式和发布订阅模式有点类似,区别在于前者只有两个对象(观察者和被观察者),后者有三个对象(发布者、Broker和订阅者),多了一个中转站。

监听消息接口

public interface IListener {
void onListen(String message);
}

观察者1

public class LaoWang implements IListener {

@Override
public void onListen(String message){
System.out.println("我是隔壁老王,收到了Observe发布的消息:"+"{"+message+"}");
}
}

观察者2

public class LaoWu implements IListener {

@Override
public void onListen(String message) {
System.out.println("我是隔壁老吴,收到了Observe发布的消息:" + "{" + message + "}");
}
}

被观察者

public class Observe {

List<IListener> listeners = new ArrayList<IListener>();

//通过传入对应的订阅者,开始订阅Observe
public void addListener(IListener iListener) {
listeners.add(iListener);
}

//Observe向观察者集合listeners发消息
public void sendMessage() {
listeners.forEach(obj -> {
obj.onListen("我是Observe推送过来的消息体");
});
}
}

测试

Observe observe = new Observe();

//老王订阅Observe
Observe observe.addListener(new LaoWang());

//Observe向订阅者发消息
observe.sendMessage();

结果:

//控制台打印:
//我是隔壁老王,收到了Observe发布的消息:{我是Observe推送过来的消息体}

装饰者模式

理解起来有点绕,参考链接1
参考2

适配器模式

定义:把一个类的接口变换成客户端所期待的另一种接口,使得原本因接口不匹配而无法在一起工作的两个类可以一起工作,即系统只有220伏的接口,我现在要用110伏,所以适配器就产生了,通过适配器把220转成110伏。
角色组成:源角色=>适配器=>目标角色

  • 类适配器模式

中国标准电压类

public class HighVoltage {

public int chinaVoltage() {
int voltage = 220;
return voltage;
}
}

高转低电压接口

public interface IHighToLow {

int change();
}

类适配器

//继承源角色并实现目标角色
public class Adapter extends HighVoltage implements IHighToLow {

@Override
public int change() {
//获取中国标准电压
int china = chinaVoltage();

//电压转换
int change = china / 2;
System.out.println("转换后的电压是:" + change + "伏");

return change;
}
}

测试

//输出:转换后的电压是:110伏
  • 对象适配器模式
    对象适配器类
    适配器实现转电压接口,重写转换方法。构造适配时,传入一个HighVoltage对象。

public class ObjAdapter implements IHighToLow {

private HighVoltage highVoltage;

public ObjAdapter(HighVoltage highVoltage) {
this.highVoltage = highVoltage;
}

@Override
public int change() {
//获取中国标准电压
int china = highVoltage.chinaVoltage();
//电压转换
int change = china / 2;
System.out.println("转换后的电压是:" + change + "伏");
return change;
}

}

Template模板模式

例如:RedisTemplate、JdbcTemplate等模板,可封装不变部分,扩展可变部分,提取公共代码,便于维护,行为由父类控制,子类实现。

游戏抽象类

//游戏抽象类,不能实例化new,只能由子类实现行为
public abstract class Game {

abstract void init();

abstract void start();

abstract void end();
//因模板是固定不变的,所以用final修饰
public final void play() {

init();
start();
end();
}

}

玩王者模板类

public class WangZhe extends Game {

@Override
void init() {
System.out.println("初始化王者");
}

@Override
void start() {
System.out.println("我开始打王者");
}

@Override
void end() {
System.out.println("我退出了王者");
}
}

玩吃鸡模板类

public class ChiJi extends Game {

@Override
public void init(){
System.out.println("初始化吃鸡");
}

@Override
public void start(){
System.out.println("我开始玩吃鸡");
}

@Override
public void end(){
System.out.println("我退出了吃鸡");
}
}

测试

Game wangZhe = new WangZhe();
wangZhe.play();
//控制台打印结果:
//初始化王者
//我开始打王者
//我退出了王者

由测试结果可知,模板模式常用来解决固定的一系列操作,比如jdbc连接数据库的步骤分为:1.加载驱动 =》2.创建连接 =》3.预处理 =》4.执行sql语句 =》5.关闭jdbc连接流