四大件——计算机网络、计算机操作系统、数据结构和算法、设计模式。而这篇文章,针对常见的几种设计模式,展开讨论,若是讨论的过浅,或者是有不对的地方,还望各位大佬多多指正。
一、设计模式的五大基本原则
1、开放封闭原则
我们的类,对于扩展应该是开放的,对于修改是封闭的。也就是说,我们支持类继承、组合等扩展方式,让类具有更多的特性,但是不能直接对类进行修改。
2、单一职责原则
单一职责,指的是一个类的变化,往往只有一个变化点,能够使类的状态发送变化,且这个变化点的方向,也就是这个类的职责所在。
3、里式替换原则
这是一个契合于多态的原则,一个类的子类,可以直接代替其父类。接口实现类应该可以对接口进行代替。
4、依赖倒置原则
高层模块不应该直接依赖于底层模块,因为两者都是变化点,两者都应该依赖于抽象
具体实现细节应该依赖于抽象
5、接口隔离原则
接口应该小而完备,那些不需要的方法,不应该强迫子类去实现
二、模板方法
这是一种组件协作模式,定义程序的执行流程,而流程的具体实现,应该延迟到子类去实现,这样子就可以在不改变父类原有流程的基础上,对父类的方法做具体实现(重载)。
1. 父类:
package templateMethod;
/**
* @author yishuai
* @version 1.0
* @description 父类
* @date 2021/10/7 16:11
*/
public abstract class Father {
public void step1(){
System.out.println("步骤一");
}
public void step2(){
System.out.println("步骤二");
}
public void step3(){
System.out.println("步骤三");
}
public abstract void step4();
public abstract void step5();
/**
* 定义好执行过程,由子类确定实现逻辑
*/
public void run(){
//稳定点
step1();
step2();
step3();
//变化点
step4();
step5();
}
}
2. 子类
package templateMethod;
/**
* @author yishuai
* @version 1.0
* @description 子类
* @date 2021/10/7 16:16
*/
public class Son extends Father{
//两个具体实现细节
@Override
public void step4() {
System.out.println("步骤4");
}
@Override
public void step5() {
System.out.println("步骤5");
}
}
3. 测试类
package templateMethod;
/**
* @author yishuai
* @version 1.0
* @description 测试类
* @date 2021/10/7 16:18
*/
public class Test {
public static void main(String[] args) {
//用父类定义具体的执行流程,对应的子类实例去实现
Father father = new Son();
father.run();
}
}
三、单例模式
手写五种单例模式,基本常考一种,会五种更强
主要思想:
1、私有化构造器
2、自行创建实例
3、对外暴露获取这个实例的方式
1. 饿汉式(线程安全)
package single;
/**
* @author yishuai
* @version 1.0
* @description 懒汉式
* @date 2021/10/7 16:24
*/
public class Demo01 {
/**
* 设置成private是保证其他类不能直接获取
* 设置成static是为了初始类就立即实例化
* 因为是随着类加载而实例化,所以是线程安全的,因为类加载只会发生一次
*/
private static Demo01 demo01 = new Demo01();
//构造器私有化
private Demo01() {
}
//直接返回实例
public static Demo01 getInstance(){
return demo01;
}
}
2. 懒汉式(线程不安全)
package single;
/**
* @author yishuai
* @version 1.0
* @description 懒汉式
* @date 2021-10-7 16:30:56
*/
public class Demo02 {
/**
* 设置成private是保证其他类不能直接获取
* 设置成static是为了让getInstance方法直接调用
*/
private static Demo02 demo02;
//构造器私有化
private Demo02() {
}
//直接返回实例,此时是线程不安全的,若要改造成线程安全,则需要在这个方法上面加上sync锁
public static Demo02 getInstance(){
//如果还没实例化,就进行实例化
if (demo02 == null){
demo02 = new Demo02();
}
return demo02;
}
}
3. 内部类(线程安全)
package single;
/**
* @author yishuai
* @version 1.0
* @description 内部类
* @date 2021/10/7 16:34
*/
public class Demo03 {
//构造器私有化
private Demo03() {
}
/**
* 通过一个静态的内部类的方式,成为Demo03的一个类属性
* 保证全局只有一个实例
*/
private static class insideSingle{
//实例化为静态常量,保证全局单一,且方便外部类调用
private static final Demo03 demo03 = new Demo03();
}
//直接返回实例
public static Demo03 getInstance(){
return insideSingle.demo03;
}
}
4. 枚举(线程安全)
package single;
/**
* @author yishuai
* @version 1.0
* @description 枚举
* @date 2021/10/7 16:42
*/
public enum Demo04 {
/**
* 枚举本身就是一个全局单实例
* 它是由JVM保证的
* 每个属性都会被 public static final修饰
*/
DEMO04;
public void xxx(){
//这里写业务逻辑,jvm初始化并保证全局单例,不可被二次初始化
}
}
5. 双重锁校验(线程安全)
package single;
/**
* @author yishuai
* @version 1.0
* @description 双重锁校验
* @date 2021/10/7 16:49
*/
public class Demo05 {
/**
* 设置成private是保证其他类不能直接获取
* 设置成static是为了让getInstance方法直接调用,且全局单一
* 设置成volatile是为了保证可见性,防止因为高速缓存,造成死锁
*/
private volatile static Demo05 demo05;
//构造器私有化
private Demo05() {
}
public static Demo05 getInstance(){
/*
让所有线程都能立即判断demo05是否为空,
这样子当demo05实例化之后,就不用去获取锁了,效率更高
*/
if (demo05 == null){
//在内部加锁,是为了防止多个线程同时争抢,造成多次实例化
synchronized (Demo05.class){
/*
再次判断是因为多个线程在争抢锁,
可能会在实例化之前,多个线程有序的进入
再次判断就可以让其他线程不会再次对Demo05实例化
*/
if (demo05 == null){
demo05 = new Demo05();
}
}
}
return demo05;
}
}
四、策略模式
通过策略模式,将程序设计的骨架,通过多态的方式,在需要的场合,在各个类型直接切换,实现对应的算法逻辑,以此来解决大量判断的问题,便于拓展,缺点是会增加许多类。
1. 抽象接口
package strategy;
/**
* @author yishuai
* @version 1.0
* @description TODO
* @date 2021/10/7 17:07
*/
public interface People {
/**
* 通过接口定义方法,让子类去实现,
* 通过多态绑定对应的子类,调用对应的业务逻辑
*/
public void eat();
public void sleep();
}
2. 黑人
package strategy;
/**
* @author yishuai
* @version 1.0
* @description 黑人
* @date 2021年10月7日17:10:06
*/
public class BlackPeople implements People{
@Override
public void eat() {
System.out.println("我是小黑人,我要吃饭了");
}
@Override
public void sleep() {
System.out.println("我是小黑人,我要睡觉觉了");
}
}
3. 白人
package strategy;
/**
* @author yishuai
* @version 1.0
* @description 白人
* @date 2021/10/7 17:09
*/
public class WhitePeople implements People{
@Override
public void eat() {
System.out.println("我是小白人,我要吃饭了");
}
@Override
public void sleep() {
System.out.println("我是小白人,我要睡觉觉了");
}
}
4. 中间类
package strategy;
/**
* @author yishuai
* @version 1.0
* @description 中间类
* @date 2021/10/7 17:12
*/
public class Proxy {
//父类
private People people;
//子类绑定父类
public Proxy(People people) {
this.people = people;
}
//通过父类调优,实际为子类具体逻辑
public void run(){
people.eat();
people.sleep();
}
}
5. 测试
package strategy;
/**
* @author yishuai
* @version 1.0
* @description TODO
* @date 2021/10/7 17:10
*/
public class Test {
public static void main(String[] args) {
//通过中间类,传入的是什么颜色的人,就会调用什么方法
Proxy proxy = new Proxy(new BlackPeople());
proxy.run();
}
}
五、工厂模式
这是一种对象创建模式,通过这种模式,避免直接去new对象,而是通过工厂来获取对应的对象实例,从而实现代码的解耦
1. 人接口
package factory;
/**
* @author yishuai
* @version 1.0
* @description 人接口
* @date 2021/10/7 17:20
*/
public interface People {
public void eat();
}
2. 黑人
package factory;
/**
* @author yishuai
* @version 1.0
* @description 黑人
* @date 2021/10/7 17:21
*/
public class BlackPeople implements People{
@Override
public void eat() {
System.out.println("小黑吃饭了");
}
}
3. 白人
package factory;
/**
* @author yishuai
* @version 1.0
* @description 白人
* @date 2021/10/7 17:21
*/
public class WhitePeople implements People{
@Override
public void eat() {
System.out.println("小白吃饭了");
}
}
4. 工厂
package factory;
/**
* @author yishuai
* @version 1.0
* @description 造人工厂
* @date 2021/10/7 17:22
*/
public class PeopleFactory {
//返回黑人
public People getBlackPeople(){
return new BlackPeople();
}
//返回白人
public People getWhitePeople(){
return new WhitePeople();
}
}
5. 测试
package factory;
/**
* @author yishuai
* @version 1.0
* @description TODO
* @date 2021/10/7 17:24
*/
public class Test {
public static void main(String[] args) {
//创建工厂
PeopleFactory peopleFactory = new PeopleFactory();
//拿到黑人实例并吃饭
peopleFactory.getBlackPeople().eat();
//拿到白人实例并吃饭
peopleFactory.getWhitePeople().eat();
}
}
六、抽象工厂模式
抽象工厂模式又被称之为超级工厂模式,这是一个创造工厂的工厂,它返回的应该是其他实例的工厂,然后由这个工厂创建实例。
1. 借用上面的工厂模式代码,完全拷贝到新的包中
2. 创建动物接口
package abstractFactory;
/**
* @author yishuai
* @version 1.0
* @description 动物接口
* @date 2021/10/7 17:32
*/
public interface Animal {
public void sleep();
}
3. 猫
package abstractFactory;
/**
* @author yishuai
* @version 1.0
* @description TODO
* @date 2021/10/7 17:34
*/
public class Cat implements Animal {
@Override
public void sleep() {
System.out.println("猫咪睡觉觉了");
}
}
4. 狗
package abstractFactory;
/**
* @author yishuai
* @version 1.0
* @description TODO
* @date 2021/10/7 17:34
*/
public class Dog implements Animal {
@Override
public void sleep() {
System.out.println("狗狗睡觉觉了");
}
}
5. 动物工厂
package abstractFactory;
/**
* @author yishuai
* @version 1.0
* @description TODO
* @date 2021/10/7 17:35
*/
public class AnimalFactory {
//获取猫实例
public Animal getCat(){
return new Cat();
}
//获取狗实例
public Animal getDog(){
return new Dog();
}
}
6. 抽象工厂
package abstractFactory;
/**
* @author yishuai
* @version 1.0
* @description TODO
* @date 2021/10/7 17:36
*/
public class AbstractFactory {
//获取动物实例工厂
public AnimalFactory getAnimalFactory(){
return new AnimalFactory();
}
//获取人实例工厂
public PeopleFactory getPeopleFactory(){
return new PeopleFactory();
}
}
7. 测试
package abstractFactory;
/**
* @author yishuai
* @version 1.0
* @description TODO
* @date 2021/10/7 17:24
*/
public class Test {
public static void main(String[] args) {
//创建抽象工厂
AbstractFactory abstractFactory = new AbstractFactory();
//通过抽象工厂拿到造人工厂
PeopleFactory peopleFactory = abstractFactory.getPeopleFactory();
//通过造人创建造一个白人,让他吃饭
peopleFactory.getWhitePeople().eat();
//通过造人创建造一个黑人,让他吃饭
peopleFactory.getBlackPeople().eat();
//通过抽象工厂拿到动物工厂
AnimalFactory animalFactory = abstractFactory.getAnimalFactory();
//通过动物工厂造一只猫,让它睡觉
animalFactory.getCat().sleep();
//通过动物工厂造一只狗,让它睡觉
animalFactory.getDog().sleep();
}
}