设计模式(Design pattern)是解决软件开发某些特定问题而提出的一些解决方案也可以理解成解决问题的一些思路。
作用:可以帮助我们增强代码的可重用性、可扩充性、 可维护性、灵活性好。
目的:实现代码的高内聚、低耦合。
贯穿思想:面向接口编程,最大限度的适用变化、实现代码复用。
针对不同类型的问题,总结出 23 种解决方案,这 23 种解决方案就是我们所说的 23 种设计模式。
其实还有两类:并发型模式和线程池模式,用一个图片来整体描述一下:
)
23 种设计默认可以归纳为 3 类:创建型(Creational)、结构型 (Structural)和行为型(Behavioral)
1. **创建型模式:**对象实例化的模式,创建型模式用于解耦对象的实例化过程。
2. 结构型模式:把类或对象结合在一起形成一个更大的结构。
3. 行为型模式:类和对象如何交互,及划分责任和算法
1:创建型模式:都是用来帮助我们创建对象的、
2:结构性模式:关注对象和类的组织
3:行为型模式:关注系统中对象之间的相互交换,研究系统在运行时对象之间的相互通信和协作,进一步明确对象的职责
基本介绍
对类来说的,即一个类应该只负责一项职责。如类A负责两个不同职责:职责1,职责2。
当职责1需求变更而改变A时,可能造成职责2执行错误,所以需要将类A的粒度分解为 A1,A2。
注意事项和细节
降低类的复杂度,一个类只负责一项职责,这个需要结合实际的开发需求。
提高类的可读性,可维护性。
降低变更引起的风险。
通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单一职责原则;只有类中方法数量足够少,可以在方法级别保持单一职责原则。
基本介绍
开闭原则(Open Closed Principle)是编程中最基础、最重要的设计原则。
一个软件实体如类,模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现扩展细节。
当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化,软件中的对象(类、模块、函数等等)应该对于扩展是开放的,但是对于修改时封闭的。
编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则。
实际案例,例如需要重新计算三角形的面积(提高常量的精度),可以使用继承的方式对类的功能进行扩展,在新类中重写计算面积的方法。有些只需要在本类中完善即可,这个要看实际的情况。
基本介绍
里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖 来解决问题。
以下程序引出的问题和思考
解决方法
- 我们发现原来运行正常的相减功能发生了错误。原因就是类B无意中重写了父类的方法,造成原有功能出现错误。在实际编程中,我们常常会通过重写父类的方法完成新的功能,这样写起来虽然简单,但整个继承体系的复用性会比较差。特别是运行多态比较频繁的时候。
2)通用的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉, 采用依赖,聚合,组合等关系代替。
基本介绍
高层模块不应该依赖低层模块,二者都应该依赖其抽象。
抽象不应该依赖细节,细节应该依赖抽象。
依赖倒转(倒置)的中心思想是面向接口编程。
依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在java中,抽象指的是接口或抽象类,细节就是具体的实现类。
使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。
注意事项和细节
低层模块尽量都要有抽象类或接口,或者两者都有,程序稳定性更好。
变量的声明类型尽量是抽象类或接口, 这样我们的变量引用和实际对象间,就存在 一个缓冲层,利于程序扩展和优化。
继承时遵循里氏替换原则。
基本介绍
客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上
分析
1)类A通过接口Interface1依赖类B,类C通过 接口Interface1依赖类D,如果接口 Interface1对于类A和类C来说不是最小接口, 那么类B和类D必须去实现他们不需要的方法。
2)按隔离原则应当这样处理。
将接口Interface1拆分为独立的几个接口, 类A和类C分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则。
接口隔离原则
应传统方法的问题和使用接口隔离原则改进
1)类A通过接口Interface1依赖类B,类C通过接口Interface1依赖类D,如果接口 Interface1对于类A和类C来说不是最小接口,那么类B和类D必须去实现他们不 需要的方法。
2)将接口Interface1拆分为独立的几个接口,类A和类C分别与他们需要的接口建立 依赖关系。也就是采用接口隔离原则。
3)接口Interface1中出现的方法,根据实际情况拆分为三个接口。
原则是尽量使用合成/聚合的方式,而不是使用继承。
核心思想
找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
针对接口编程,而不是针对实现编程。
为了交互对象之间的松耦合设计而努力。
4)设计原则应该结合实际场景。
基本介绍
一个对象应该对其他对象保持最少的了解。
类与类关系越密切,耦合度越大。
迪米特法则(Demeter Principle)又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的public 方法,不对外泄露任何信息。
迪米特法则还有个更简单的定义:只与直接的朋友通信。
注意事项和细节
迪米特法则的核心是降低类之间的耦合,实现高类聚,低耦合。
但是注意:由于每个类都减少了不必要的依赖,因此迪米特法则只是要求降低类间(对象间)耦合关系, 并不是要求完全没有依赖关系。
单例模式是指在整个应用中一个类的对象只允许出现一个(类的对象最多只允许创建一次);
我们在创建一个类的对象时,调用的是类的构造器,所以在单例中类的构造器只允许调用一次
**核心:**构造方法私有化,不允许在外部创建类的实例,而将类的实例构建放到类内部实现,在内部通过编码控制实例的创建只能创建一次。
单例模式的实现:
结构
public class Instance1 {
/*饿汉(勤汉)模式:它是一种对象立即加载(对象立即创建),当类被加载时,
该单例对象就被创建,时间效率比较高,空间效率低。
*/
//静态变量只会被加载一次
private static Instance1 instance = new Instance1();
//构造器私有化
private Instance1() {
}
// 3 提供返回类对象的静态方法
public static Instance1 getInstance() {
return instance;
}
}
测试类
public class App {
private static void a() {
Instance1 instance1 = Instance1.getInstance();
Instance1 instance2 = Instance1.getInstance();
System.out.println(instance1);
System.out.println(instance2);
System.out.println(instance2 == instance1);
}
public static void main(String[] args) {
a();
}
}
测试结果
//懒汉模式:它使用的是懒加载模式实现的实例,当需要使用类对象时才创建该类的单例对象(时间效率比较低,空间效率高)
public class Instance2 {
//为了不让其他类直接访问该成员 懒汉式单例,在使用时创建对象
//1、私有静态变量
private static Instance2 instance;
//2、将构造器私有化
public Instance2() {
}
//3、提供一个静态方法,并返回该类的对象
public static Instance2 getInstance() {
if (instance == null) {
instance = new Instance2();
}
return instance;
}
}
测试类
public class App {
private static void b() {
Instance2 instance1 = Instance2.getInstance();
Instance2 instance2 = Instance2.getInstance();
System.out.println(instance2);
System.out.println(instance1);
System.out.println(instance2 == instance1);
}
public static void main(String[] args) {
b();
}
}
测试结果
懒加载会出现问题:在多线程的情况下,同时会new多个对象,不满足单例模式。
//懒汉模式:它使用的是懒加载模式实现的实例,当需要使用类对象时才创建该类的单例对象(时间效率比较低,空间效率高)
public class Instance2 {
//为了不让其他类直接访问该成员 懒汉式单例,在使用时创建对象
//1、私有静态变量
private static Instance2 instance;
//2、将构造器私有化
public Instance2() {
}
//3、提供一个静态方法,并返回该类的对象
@SneakyThrows
public static Instance2 getInstance() {
if (instance == null) {
Thread.sleep(10);
instance = new Instance2();
}
return instance;
}
}
测试类
public class App {
//
@SneakyThrows
private static void error1() {
AtomicReference<Instance2> instance1 = new AtomicReference<>();
AtomicReference<Instance2> instance2 = new AtomicReference<>();
Thread t1 = new Thread(() -> {
instance1.set(Instance2.getInstance());
});
Thread t2 = new Thread(() -> {
instance2.set(Instance2.getInstance());
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(instance1.get());
System.out.println(instance2.get());
System.out.println(instance1.get() == instance2.get());
}
public static void main(String[] args) {
error1();
}
}
测试结果
针对以上出现的问题,有一个解决方案:双重校验锁模式
// 双重校验锁模式:它是针对懒汉模式的一种优化,提高效率
public class Instance3 {
private static Instance3 instance;
private static Object obj = new Object();
public Instance3() {
}
@SneakyThrows
public static Instance3 getInstance() {
if (instance == null) {
//用于除了第一次创建对象的其他人
synchronized (obj) {
if (instance == null) {
//用于第一次创建对象
Thread.sleep(10);
instance = new Instance3();
}
}
}
return instance;
}
}
测试类
@SneakyThrows
private static void c() {
AtomicReference<Instance3> instance1 = new AtomicReference<>();
AtomicReference<Instance3> instance2 = new AtomicReference<>();
Thread t1 = new Thread(() -> {
instance1.set(Instance3.getInstance());
});
Thread t2 = new Thread(() -> {
instance2.set(Instance3.getInstance());
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(instance1.get());
System.out.println(instance2.get());
System.out.println(instance1.get() == instance2.get());
}
测试结果
但是对于以上三种遇到反射,就失效了
测试类
public class App {
//
@SneakyThrows
private static void error2() {
// Class c = Class.forName("com.dailyblue.spring.单例模式.Instance1");
// Class c = Class.forName("com.dailyblue.spring.单例模式.Instance2");
Class c = Class.forName("com.dailyblue.spring.单例模式.Instance3");
Constructor constructor = c.getDeclaredConstructor();
constructor.setAccessible(true);
Object obj1 = constructor.newInstance();
Object obj2 = constructor.newInstance();
System.out.println(obj1);
System.out.println(obj2);
System.out.println(obj1 == obj2);
}
public static void main(String[] args) {
error2();
}
}
测试结果
// 最简单,最使用,最推荐的写法,使用枚举实现单例
public enum Instance4 {
INSTANCE;
public void a(){
System.out.println("aaaa");
}
public void b() {
System.out.println(this);
}
}
测试类
public class App {
private static void d() {
Instance4 instance1 = Instance4.INSTANCE;
Instance4 instance2 = Instance4.INSTANCE;
Instance4 instance3 = Instance4.INSTANCE;
Instance4 instance4 = Instance4.INSTANCE;
System.out.println(instance1);
System.out.println(instance2);
System.out.println(instance3);
System.out.println(instance4);
System.out.println(instance1 == instance2);
System.out.println(instance3 == instance4);
System.out.println(instance1 == instance3);
instance1.a();
instance1.b();
}
public static void main(String[] args) {
d();
}
}
测试结果
工厂模式分为简单工厂模式、工厂方法模式和抽象工厂模式,它们都属于设计模式中的创建型模式。
主要功能:帮助我们把对象的实例化部分抽取了出来
目的:降低系统中代码耦合度,并且增强了系统的扩展性。
1. 简单工厂模式
优点:实现对象的创建和对象的使用分离,将对象的创建交给专门的工厂类负责,
缺点:在于工厂类不够灵活,增加新的具体产品时需要修改工厂类的判断逻辑代码,而且产品较多时,工厂方法代码逻辑将会非常复杂。
结构
定义一个工厂类
public class PhoneFactory {
public Phone makePhone(String name){
return switch (name) {
case "小米" -> new MiPhone();
case "华为" -> new MetaPhone();
case "苹果" -> new IPhone();
default -> null;
};
}
}
定义创建产品对象的接口
//抽象产品
public interface Phone {
void show();
}
创建具体产品实例
public class MiPhone implements Phone{
@Override
public void show() {
System.out.println("我是小米手机");
}
}
public class MetaPhone implements Phone{
@Override
public void show() {
System.out.println("我是华为手机");
}
}
public class IPhone implements Phone{
@Override
public void show() {
System.out.println("我是苹果手机");
}
}
测试类
public class App {
public static void main(String[] args) {
Phone p = new PhoneFactory().makePhone("苹果");
p.show();
}
}
测试结果
2. 工厂方法模式
此模式中,通过定义一个抽象的核心工厂类,并定义创建产品对象的接口,创建具体产品实例的工作延迟到其工厂子类去完成。
好处:核心类只关注工厂类的接口定义,而具体的产品实例交给具体的工厂子类去创建。
当系统需要新增一个产品时,无需修改现有系统代码,只需要添加一个具体产品类和其对应的工厂子类,使系统的扩展性变得很好,符合面向对象编程的开闭原则。
结构
定义一个抽象的核心工厂类
// 抽象工厂
public interface ComputerFactory {
Computer makeComputer();
}
定义创建产品对象的接口
//抽象产品
public interface Computer {
void show();
}
具体的产品实例
public class MacComputer implements Computer {
@Override
public void show() {
System.out.println("我是苹果电脑");
}
}
public class MetaComputer implements Computer{
@Override
public void show() {
System.out.println("我是华为电脑");
}
}
public class MiComputer implements Computer{
@Override
public void show() {
System.out.println("我是小米电脑");
}
}
具体的工厂子类
public class MacFactory implements ComputerFactory{
@Override
public Computer makeComputer() {
return new MacComputer();
}
}
public class MateFactory implements ComputerFactory {
@Override
public Computer makeComputer() {
return new MacComputer();
}
}
public class MiFactory implements ComputerFactory{
@Override
public Computer makeComputer() {
return new MiComputer();
}
}
测试类
public class App {
public static void main(String[] args) {
ComputerFactory factory = new MacFactory();
factory.makeComputer().show();
}
}
测试结果
3. 抽象工厂模式
此模式是对工厂方法模式的进一步扩展。在工厂方法模式中,一个具体的工厂负责生产一类具体的产品,即一对一的关系。
但是,如果需要一个具体的工厂生产多种产品对象,那么就需要用到抽象工厂模式了。
结构
定义创建产品对象的接口
// 抽象面条
public interface Noodles {
void shape();
}
// 抽象拌料
public interface MixingMaterials {
void materials();
}
定义一个抽象的核心工厂类
//抽象面馆(工厂)
public interface NoodleRestaurant {
Noodles getNoodles();
MixingMaterials getMixingMaterials();
}
具体的工厂
//具体面馆(工厂)
public class XiaoZhangNoodleRestaurant implements NoodleRestaurant {
@Override
public Noodles getNoodles() {
return new Spaghetti();
}
@Override
public MixingMaterials getMixingMaterials() {
return new OilSpillMixingMaterials();
}
}
具体的对象实例
public class Lasagna implements Noodles{
@Override
public void shape() {
System.out.println("宽面条");
}
}
public class Spaghetti implements Noodles{
@Override
public void shape() {
System.out.println("细面条");
}
}
public class SpinachNoodles implements Noodles{
@Override
public void shape() {
System.out.println("菠菜面");
}
}
public class OilSpillMixingMaterials implements MixingMaterials {
@Override
public void materials() {
System.out.println("油泼");
}
}
public class TomatoAndEgg implements MixingMaterials{
@Override
public void materials() {
System.out.println("西红柿鸡蛋");
}
}
测试类
public class App {
public static void main(String[] args) {
NoodleRestaurant noodleRestaurant = new XiaoZhangNoodleRestaurant();
Noodles noodles = noodleRestaurant.getNoodles();
MixingMaterials mixingMaterials = noodleRestaurant.getMixingMaterials();
noodles.shape();
mixingMaterials.materials();
}
}
测试结果
代理模式属于结构型设计模式
代理模式是指给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
通俗的来讲代理模式就是我们生活中常见的中介。
代理模式分类:
1. 静态代理模式
静态代理是指代理类由开发人员创建,在程序运行前代理对象已被创建;
(1) 创建服务类接口
public interface Treat {
void treat();
}
(2) 实现服务类接口(代理的目标对象)
public class Boss implements Treat{
@Override
public void treat() {
System.out.println("我是老板,今天我请客!");
}
}
(3)创建代理类(代理对象)
在代理对象中调用目标对象的操作方法,并在目标方法调用前或调用后执行相应的功能
public class Assistant implements Treat {
private Boss boss;
public Assistant(Boss boss) {
this.boss = boss;
}
@Override
public void treat() {
System.out.println("今天老板不来,我全权负责");
boss.treat();
System.out.println("大家吃好喝好");
}
}
(4)测试类
public class App {
public static void main(String[] args) {
Boss boss = new Boss();
Assistant assistant = new Assistant(boss);
assistant.treat();
}
}
测试结果
2. 动态代理模式(基于 JDK 实现)
动态代理是指代理对象在程序运行时通过反射机制(Proxy)动态创建。
动态代理中我们不再需要再手动的创建代理类,我们只需要编写一个**动态处理器(InvocationHandler)**就可以了。真正的代理对象由 JVM 在运行时为我们动态的来创建。
JDK 实现的动态代理只支持接口代理,不支持类的代理
结构
创建服务类接口
public interface Treat {
void treat();
}
实现服务类接口(代理的目标对象)
public class Boss implements Treat{
@Override
public void treat() {
System.out.println("我是老板,今天我请客!");
}
}
动态处理器类
public class DynamicProxyHandler implements InvocationHandler {
private Treat boss;
public DynamicProxyHandler(Treat boss) {
this.boss = boss;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("我是代理者,我来代表老板");
Object result = method.invoke(boss, args);
System.out.println("大家好!");
return result;
}
}
测试类
public class App {
public static void main(String[] args) {
Boss boss = new Boss();
Treat obj = (Treat) Proxy.newProxyInstance(Treat.class.getClassLoader(), new Class[]{
Treat.class}, new DynamicProxyHandler(boss));
obj.treat();
}
}
测试类
3.动态代理模式(CGLIB 代理)
支持使用类的子类做为代理对象。
CGLIB 采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对 final 修饰的类进行代理。
JDK 动态代理与 CGLib 动态代理均是实现 Spring AOP 的基础。Enhancer 是一个非常重要的类,它允许为非接口类型创建一个 JAVA 代理,Enhancer 动态的创建给定类的子类并且拦截代理类的所有的方法,和 JDK 动态代理不一样的是不管是接口还是类它都能正常工作。
结构
创建服务类接口
public interface Treat {
void treat();
}
实现服务类接口(代理的目标对象)
public class Boss implements Treat {
@Override
public void treat() {
System.out.println("我是老板,请客吃法我掏钱!");
}
}
创建代理类(代理对象)
public class CgLibProxy implements MethodInterceptor {
private Boss boss;
public Object getEnhancer(Boss boss) {
this.boss = boss;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.boss.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("我今天代表老板");
Object result = method.invoke(boss, objects);
System.out.println("再见!");
return result;
}
}
测试类
public class App {
public static void main(String[] args) {
Boss boss = new Boss();
CgLibProxy cgLibProxy = new CgLibProxy();
Boss obj = (Boss) cgLibProxy.getEnhancer(boss);
obj.treat();
}
}
测试结果
适配器模式是指将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作。(转换器)
结构
(1) 电源类(模拟电源,输出电压为 220V)
public class Power {
public int output() {
System.out.println("家庭额定电压是:220V");
return 220;
}
}
(2) 手机充电接口(模拟客户接口,输出电压为 5V)
//手机充电接口
public interface PhoneCharge {
int charge();
}
(3) 手机类(模拟客户类,通过手机充电接口为手机充电)
public class Phone {
public void charge(PhoneCharge phoneCharge) {
System.out.println("手机开始充电了");
int o = phoneCharge.charge();
System.out.println("充电电压是:" + o + "V");
}
}
(4) 适配器类(模拟手机充电器,用于将 220V 转换为 5V 电压)
public class PowerAdapter implements PhoneCharge {
private Power power = new Power();
@Override
public int charge() {
System.out.println("通过充电器开始给手机充电");
int ed = power.output();
System.out.println("开始将" + ed + "V转换成5V");
return 5;
}
}
(5) 测试类
public class App {
public static void main(String[] args) {
Phone phone = new Phone();
PowerAdapter adapter = new PowerAdapter();
phone.charge(adapter);
}
}
测试结果
策略模式(Strategy):定义了一组算法,将每个算法都封装起来,在使用时根据不同的环境使用不同的算法(使用时可以互换),UML 结构图如下:
1. Context 上下文
Context 是实际要处理功能的类,该类中屏蔽了对策略或算法的直接访问,通过抽象策略来访问具体策略(具体策略可能会变化)。
2. Strategy 抽象策略(一般为接口)
Strategy 是对具体策略或算法的抽象
3. ConcreteStrategy 具体策略
ConcreteStrategy 是对抽象策略的实现,即具体算法的实现,实现抽象策略的实现类。
示例:
我们出去旅游的时候可能有很多种出行方式,比如说我们可以坐火车、坐高铁、坐飞机等等。不管我们使用哪一种出行方式,最终的目的地都是一样的。也就是选择不同的方式产生的结果都是一样的。
结构
(1)旅游类(Context)
//Context上下文
public class Travel {
private Strategy strategy;
public Travel(Strategy strategy) {
this.strategy = strategy;
}
public void play() {
System.out.println("我们一起要去三亚玩!");
strategy.travelTools();
System.out.println("三亚很好玩!");
}
}
(2)Strategy 抽象策略
// 出行方式:抽象策略
public interface Strategy {
void travelTools();
}
(3)ConcreteStrategy 具体策略
public class CarStrategy implements Strategy{
@Override
public void travelTools() {
System.out.println("开车自驾游");
}
}
public class TrainStrategy implements Strategy{
@Override
public void travelTools() {
System.out.println("开始坐火车!");
}
}
public class AircraftStrategy implements Strategy {
@Override
public void travelTools() {
System.out.println("开始登记,飞机起飞了");
}
}
测试类
public class App {
public static void main(String[] args) {
CarStrategy car = new CarStrategy();
TrainStrategy train = new TrainStrategy();
AircraftStrategy air = new AircraftStrategy();
// Travel travel1 = new Travel(car);
// Travel travel2 = new Travel(train);
Travel travel3 = new Travel(air);
// travel1.play();
// travel2.play();
travel3.play();
}
}
测试结果
**责任链模式:**顾名思义,就是用来处理相关事务责任的一条执行链,执行链上有多个节点,每个节点都有机会处理请求事务,如果某个节点处理完了就可以根据实际业务需求传递给下一个节点继续处理或者返回处理完毕。
示例:
员工请假审批流程:
请假时间 1 天以内,项目组长审批即可
请假时间大于 1 天小于等于 3 天,则需要项目经理审批
请假时间大于 3 天,则需要总经理审批
(1) 抽象处理类
public abstract class Handler {
private Handler nextHandler;
public Handler(Handler nextHandler) {
this.nextHandler = nextHandler;
}
public Handler getNextHandler() {
return nextHandler;
}
public void setNextHandler(Handler nextHandler) {
this.nextHandler = nextHandler;
}
public abstract void action(int day);
}
(2) 项目组长处理者类
//项目组长
public class PM extends Handler {
public PM(Handler nextHandler) {
super(nextHandler);
}
@Override
public void action(int day) {
System.out.println("小组长开始审批");
if (day > 1) {
getNextHandler().action(day);
}
}
}
(3) 项目经理处理者类
//项目经理
public class ProjectManager extends Handler {
public ProjectManager(Handler nextHandler) {
super(nextHandler);
}
@Override
public void action(int day) {
System.out.println("项目经理开始审批");
if (day > 3) {
getNextHandler().action(day);
}
}
}
(4) 总经理处理者类
//总经理
public class CEO extends Handler {
public CEO(Handler nextHandler) {
super(nextHandler);
}
@Override
public void action(int day) {
System.out.println("总经理开始审批");
}
}
(5) 测试类
public class App {
public static void main(String[] args) {
CEO ceo = new CEO(null);
ProjectManager projectManager = new ProjectManager(ceo);
PM pm = new PM(projectManager);
// pm.action(1);
// pm.action(3);
pm.action(8);
}
}
测试结果
装饰者模式:动态地将责任(功能)附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
装饰者和被装饰对象有相同的超类。
你可以用一个或多个装饰者包装一个对象(被装饰者对象)。
既然装饰者和被装饰对象有相同的超类,所以在任何需要原始对象(被包装的)的场合,可以用装饰过的对象代替它。
装饰者可以在所委托被装饰者的行为之前与 / 或之后,加上自己的行为,以达到特定的目的。
在装饰者的行为(方法中)发起对被装饰者对象的行为(方法)的调用
对象可以在任何时候被装饰,所以可以在运行时动态地、不限量地用你喜欢的装饰者来装饰对象。
示例:
定义一个人类
学生继承人类,学生在学习
程序员继承人类,程序员在敲代码
学生和程序员不单单会学习和敲代码他们其中有些人可能还具有其他能力,
如唱歌、跳舞等,我们通过装饰模式的来为不同的人群添加新的能力
结构
(1) 人类,被装饰者的抽象
public interface Person {
void action();
}
(2) 被装饰者的具体类,直接实现 Perosn 接口
public class Programmer implements Person {
private String name;
public Programmer(String name) {
this.name = name;
}
@Override
public void action() {
System.out.println(name + "正在疯狂刷礼物!");
}
}
public class Singer implements Person {
private Programmer programmer;
public Singer(Programmer programmer) {
this.programmer = programmer;
}
@Override
public void action() {
System.out.println("我扩展了程序员的行为");
programmer.action();
System.out.println("我们一起唱着歌");
}
}
(3) 装饰器类的抽象,实现 Person 接口
public class Student implements Person {
private String name;
public Student(String name) {
this.name = name;
}
@Override
public void action() {
System.out.println(name + "正在跳舞!");
}
}
public class Student implements Person {
private String name;
public Student(String name) {
this.name = name;
}
@Override
public void action() {
System.out.println(name + "正在跳舞!");
}
}
(5) 测试类
public class App {
public static void main(String[] args) {
Programmer programmer = new Programmer("乔杰");
Singer singer = new Singer(programmer);
singer.action();
Student stu = new Student("董雪");
Dancer dancer = new Dancer(stu);
dancer.action();
}
}
测试结果
观察者模式:指多个对象之间存在一对多的依赖关系,当一个对象(服务器|主题)的状态发生改变时,所有依赖它的对象(客户端|观察者)都能自动收到通知并根据通知来决定自己行为。这种模式有时又被称为”发布-订阅模式”、“模型-视图模式”。
观察者模式属于行为型模式。观察者模式符合依赖倒置原则
观察者模式中的角色:
1、抽象主题(Subject):
它是观察者关注的事情(观察者关注的某件事),在主题中会定义”注册观察者”、”解除观察者”、”通知观察者”的相关操作,一般使用接口表示。
2、具体主题(Concrete Subject):
具体主题实现抽象主题,并指定具体的关注事件,并设置一个用于存储观察者集合
3、抽象观察者(Observer):
定义观察者收到通知后要做的事情
4、具体观察者(Concrete Observer):
具体观察者实现抽象观察者接口,对接收到通知后要做的事情进行具体实现。
实例:
购买彩票
观察者关注的主题:彩票开奖信息发布
观察者是彩票的购买人
结构
抽象主题:
public interface Subject {
// 注册观察者对象
void registerObServer(ObServer obServer);
// 从主题中移除一个观察者对象
void removeObServer(ObServer obServer);
// 通知观察者
void notifyObServer();
}
抽象观察者:
public interface ObServer {
//观察者接收通知的方法
void update(String message, int num);
}
具体主题(福彩):
public class FuCaiZhuTi implements Subject {
private Integer num;
public FuCaiZhuTi() {
num = (int) (Math.random() * 100 + 1);
}
//使用 list 集合存储所有观察者
private List<ObServer> list = new ArrayList<>();
@Override
public void registerObServer(ObServer obServer) {
//将新的观察者对象存入到观察者集合中
list.add(obServer);
}
@Override
public void removeObServer(ObServer obServer) {
//观察者集合中存在该观察者对象则移除
list.remove(obServer);
}
@Override
public void notifyObServer() {
// 通知所有观察者
list.forEach((e) -> {
e.update("摇奖结果是:" + num, num);
});
}
}
具体观察者:
public class CaiMin implements ObServer {
//设置当前观察者所关注的主题
private Integer num;
private String name;
public CaiMin(String name) {
this.name = name;
num = (int) (Math.random() * 100 + 1);
}
@Override
public void update(String message, int num) {
System.out.println(name + "购买的彩票是:" + this.num);
System.out.println("彩票中心的摇奖结果是:" + num);
System.out.println(num == this.num ? "中奖了" : "没中奖");
}
}
测试类:
public class App {
public static void main(String[] args) throws Exception {
Subject subject = new FuCaiZhuTi();
for (int i = 1; i < 251; i++) {
CaiMin caiMin = new CaiMin("屈鹏飞" + i);
subject.registerObServer(caiMin);
}
System.out.println("开始摇奖:");
Thread.sleep(2000);
subject.notifyObServer();
}
}
测试结果
文章浏览阅读3.4k次,点赞8次,收藏42次。一、什么是内部类?or 内部类的概念内部类是定义在另一个类中的类;下面类TestB是类TestA的内部类。即内部类对象引用了实例化该内部对象的外围类对象。public class TestA{ class TestB {}}二、 为什么需要内部类?or 内部类有什么作用?1、 内部类方法可以访问该类定义所在的作用域中的数据,包括私有数据。2、内部类可以对同一个包中的其他类隐藏起来。3、 当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷。三、 内部类的分类成员内部_成员内部类和局部内部类的区别
文章浏览阅读118次。分布式系统要求拆分分布式思想的实质搭配要求分布式系统要求按照某些特定的规则将项目进行拆分。如果将一个项目的所有模板功能都写到一起,当某个模块出现问题时将直接导致整个服务器出现问题。拆分按照业务拆分为不同的服务器,有效的降低系统架构的耦合性在业务拆分的基础上可按照代码层级进行拆分(view、controller、service、pojo)分布式思想的实质分布式思想的实质是为了系统的..._分布式系统运维工具
文章浏览阅读174次。1.数据源准备2.数据处理step1:数据表处理应用函数:①VLOOKUP函数; ② CONCATENATE函数终表:step2:数据透视表统计分析(1) 透视表汇总不同渠道用户数, 金额(2)透视表汇总不同日期购买用户数,金额(3)透视表汇总不同用户购买订单数,金额step3:讲第二步结果可视化, 比如, 柱形图(1)不同渠道用户数, 金额(2)不同日期..._exce l趋势分析数据量
文章浏览阅读3.3k次。堡垒机可以为企业实现服务器、网络设备、数据库、安全设备等的集中管控和安全可靠运行,帮助IT运维人员提高工作效率。通俗来说,就是用来控制哪些人可以登录哪些资产(事先防范和事中控制),以及录像记录登录资产后做了什么事情(事后溯源)。由于堡垒机内部保存着企业所有的设备资产和权限关系,是企业内部信息安全的重要一环。但目前出现的以下问题产生了很大安全隐患:密码设置过于简单,容易被暴力破解;为方便记忆,设置统一的密码,一旦单点被破,极易引发全面危机。在单一的静态密码验证机制下,登录密码是堡垒机安全的唯一_horizon宁盾双因素配置
文章浏览阅读7.7k次,点赞4次,收藏16次。Chrome作为一款挺不错的浏览器,其有着诸多的优良特性,并且支持跨平台。其支持(Windows、Linux、Mac OS X、BSD、Android),在绝大多数情况下,其的安装都很简单,但有时会由于网络原因,无法安装,所以在这里总结下Chrome的安装。Windows下的安装:在线安装:离线安装:Linux下的安装:在线安装:离线安装:..._chrome linux debian离线安装依赖
文章浏览阅读153次。中国发达城市榜单每天都在刷新,但无非是北上广轮流坐庄。北京拥有最顶尖的文化资源,上海是“摩登”的国际化大都市,广州是活力四射的千年商都。GDP和发展潜力是衡量城市的数字指...
文章浏览阅读3.3k次。前言spark在java使用比较少,多是scala的用法,我这里介绍一下我在项目中使用的代码配置详细算法的使用请点击我主页列表查看版本jar版本说明spark3.0.1scala2.12这个版本注意和spark版本对应,只是为了引jar包springboot版本2.3.2.RELEASEmaven<!-- spark --> <dependency> <gro_使用java调用spark注册进去的程序
文章浏览阅读4.8k次。汽车零部件开发工具巨头V公司全套bootloader中UDS协议栈源代码,自己完成底层外设驱动开发后,集成即可使用,代码精简高效,大厂出品有量产保证。:139800617636213023darcy169_uds协议栈 源代码
文章浏览阅读4.6k次,点赞20次,收藏148次。AUTOSAR基础篇之OS(下)前言首先,请问大家几个小小的问题,你清楚:你知道多核OS在什么场景下使用吗?多核系统OS又是如何协同启动或者关闭的呢?AUTOSAR OS存在哪些功能安全等方面的要求呢?多核OS之间的启动关闭与单核相比又存在哪些异同呢?。。。。。。今天,我们来一起探索并回答这些问题。为了便于大家理解,以下是本文的主题大纲:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JCXrdI0k-1636287756923)(https://gite_autosar 定义了 5 种多核支持类型
文章浏览阅读2.2k次,点赞6次,收藏14次。原因:自己写的头文件没有被加入到方案的包含目录中去,无法被检索到,也就无法打开。将自己写的头文件都放入header files。然后在VS界面上,右键方案名,点击属性。将自己头文件夹的目录添加进去。_vs2013打不开自己定义的头文件
文章浏览阅读3.3w次,点赞80次,收藏342次。此时,可以将系统中所有用户的 Session 数据全部保存到 Redis 中,用户在提交新的请求后,系统先从Redis 中查找相应的Session 数据,如果存在,则再进行相关操作,否则跳转到登录页面。此时,可以将系统中所有用户的 Session 数据全部保存到 Redis 中,用户在提交新的请求后,系统先从Redis 中查找相应的Session 数据,如果存在,则再进行相关操作,否则跳转到登录页面。当数据量很大时,count 的数量的指定可能会不起作用,Redis 会自动调整每次的遍历数目。_redis命令
文章浏览阅读449次,点赞3次,收藏3次。URP的设计目标是在保持高性能的同时,提供更多的渲染功能和自定义选项。与普通项目相比,会多出Presets文件夹,里面包含着一些设置,包括本色,声音,法线,贴图等设置。全局只有主光源和附加光源,主光源只支持平行光,附加光源数量有限制,主光源和附加光源在一次Pass中可以一起着色。URP:全局只有主光源和附加光源,主光源只支持平行光,附加光源数量有限制,一次Pass可以计算多个光源。可编程渲染管线:渲染策略是可以供程序员定制的,可以定制的有:光照计算和光源,深度测试,摄像机光照烘焙,后期处理策略等等。_urp渲染管线