Java23种设计模式
设计模式
一、单例设计模式
单例设计模式介绍
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例, 并且该类只提供一个取得其对象实例的方法(静态方法)。
比如 Hibernate 的 SessionFactory,它充当数据存储源的代理,并负责创建 Session 对象。SessionFactory 并不是轻量级的,一般情况下,一个项目通常只需要一个 SessionFactory 就够,这是就会使用到单例模式。
单例设计模式8种方式
单例模式有八种方式:
1) 饿汉式(静态常量)
2) 饿汉式(静态代码块)
3) 懒汉式(线程不安全)
4) 懒汉式(线程安全,同步方法)
5) 懒汉式(线程安全,同步代码块)
6) 双重检查
7) 静态内部类
8) 枚举
饿汉式(静态常量)
饿汉式(静态常量)应用实例步骤如下:
-
构造器私有化 (防止 new )
-
类的内部创建对象
-
向外暴露一个静态的公共方法。getInstance
-
代码实现
public class SingletonTest01 {public static void main(String[] args) {Singleton instance = Singleton.getInstance();Singleton instance2 = Singleton.getInstance();System.out.println(instance == instance2); // trueSystem.out.println("instance.hashCode=" + instance.hashCode());//instance.hashCode=1229416514System.out.println("instance2.hashCode=" + instance2.hashCode());//instance2.hashCode=1229416514}
}//饿汉式(静态常量)
class Singleton {//1. 构造器私有化, 外部不能newprivate Singleton() {}//2.本类内部创建对象实例private final static Singleton instance = new Singleton();//3. 提供一个公有的静态方法,返回实例对象public static Singleton getInstance() {return instance;}
}
Ø 优缺点说明:
-
优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。
-
缺点:在类装载的时候就完成实例化,没有达到 Lazy Loading 的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费
-
这种方式基于 classloder 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,在单例模式中大多数都是调用 getInstance 方法, 但是导致类装载的原因有很多种,因此不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 就没有达到 lazy loading 的效果
-
结论:这种单例模式可用,可能造成内存浪费
饿汉式(静态代码块)
Ø 代码演示:
public class SingletonTest02 {public static void main(String[] args) {// 测试Singleton instance = Singleton.getInstance();Singleton instance2 = Singleton.getInstance();System.out.println(instance == instance2); // trueSystem.out.println("instance.hashCode=" + instance.hashCode());System.out.println("instance2.hashCode=" + instance2.hashCode());}
}
//饿汉式(静态代码块)
class Singleton {//1. 构造器私有化, 外部能 new private Singleton() {}//2.本类内部创建对象实例private static Singleton instance;static {// 在静态代码块中,创建单例对象instance = new Singleton();}//3. 提供一个公有的静态方法,返回实例对象public static Singleton getInstance() {return instance;}
}
Ø 优缺点说明:
-
这种方式和上面的方式其实类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,就执行静态代码块中的代码,初始化类的实例。优缺点和上面是一样的。
-
结论:这种单例模式可用,但是可能造成内存浪费
懒汉式(线程不安全)
Ø 代码演示:
public class SingletonTest03 {public static void main(String[] args) {System.out.println("懒汉式 1 , 线程不安全~");Singleton instance = Singleton.getInstance();Singleton instance2 = Singleton.getInstance();System.out.println(instance == instance2); // trueSystem.out.println("instance.hashCode=" + instance.hashCode());System.out.println("instance2.hashCode=" + instance2.hashCode());}
}
//懒汉式(线程不安全)
class Singleton {private static Singleton instance;private Singleton() {} //提供一个静态的公有方法,当使用到该方法时,才去创建 instance//即懒汉式public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}
Ø 优缺点说明:
-
起到了 Lazy Loading 的效果,但是只能在单线程下使用。
-
如果在多线程下,一个线程进入了 if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式
-
结论:在实际开发中,不要使用这种方式.
懒汉式(线程安全,同步方法)
Ø 代码演示:
public class SingletonTest04 {public static void main(String[] args) {System.out.println("懒汉式 2 , 线程安全~");Singleton instance = Singleton.getInstance();Singleton instance2 = Singleton.getInstance();System.out.println(instance == instance2); // trueSystem.out.println("instance.hashCode=" + instance.hashCode());System.out.println("instance2.hashCode=" + instance2.hashCode());}
}
// 懒汉式(线程安全,同步方法)
class Singleton {private static Singleton instance;private Singleton() {}//提供一个静态的公有方法,加入同步处理的代码,解决线程安全问题 即懒汉式public static synchronized Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}
Ø 优缺点说明:
-
解决了线程安全问题
-
效率太低了,每个线程在想获得类的实例时候,执行 getInstance()方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接 return 就行了。方法进行同步效率太低
-
结论:在实际开发中,不推荐使用这种方式
懒汉式(线程安全,同步代码块)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pYW4RInd-1598954057032)(C:\Users\lisensen\AppData\Roaming\Typora\typora-user-images\image-20200730172624795.png)]
不推荐使用
双重检查
Ø 代码演示
public class SingletonTest06 {public static void main(String[] args) {System.out.println("双重检查");Singleton instance = Singleton.getInstance();Singleton instance2 = Singleton.getInstance();System.out.println(instance == instance2); // trueSystem.out.println("instance.hashCode=" + instance.hashCode());System.out.println("instance2.hashCode=" + instance2.hashCode());}
} // 懒汉式(线程安全,同步方法) class Singleton {private static volatile Singleton instance;private Singleton() {}//提供一个静态的公有方法,加入双重检查代码,解决线程安全问题, 同时解决懒加载问题//同时保证了效率, 推荐使用 public static synchronized Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}
}
Ø 优缺点说明:
-
Double-Check 概念是多线程开发中常使用到的,如代码中所示,我们进行了两次 if (singleton == null)检查,这样就可以保证线程安全了。
-
这样,实例化代码只用执行一次,后面再次访问时,判断 if (singleton == null),直接 return 实例化对象,也避免的反复进行方法同步.
-
线程安全;延迟加载;效率较高
-
结论:在实际开发中,推荐使用这种单例设计模式
静态内部类
Ø 代码演示:
public class SingletonTest07 {public static void main(String[] args) {System.out.println("使用静态内部类完成单例模式");Singleton instance = Singleton.getInstance();Singleton instance2 = Singleton.getInstance();System.out.println(instance == instance2); // true System.out.println("instance.hashCode=" + instance.hashCode());System.out.println("instance2.hashCode=" + instance2.hashCode());}
}
// 静态内部类完成, 推荐使用
class Singleton {private static volatile Singleton instance; //构造器私有化private Singleton() {} //写一个静态内部类,该类中有一个静态属性 Singleton private static class SingletonInstance {private static final Singleton INSTANCE = new Singleton();} //提供一个静态的公有方法,直接返回SingletonInstance.INSTANCEpublic static synchronized Singleton getInstance() {return SingletonInstance.INSTANCE;}
}
Ø 优缺点说明:
-
这种方式采用了类装载的机制来保证初始化实例时只有一个线程。
-
静态内部类方式在 Singleton 类被装载时并不会立即实例化,而是在需要实例化时,调用 getInstance 方法,才会装载 SingletonInstance 类,从而完成 Singleton 的实例化。
-
类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM 帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
-
优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高
-
结论:推荐使用.
枚举
Ø 代码演示
public class SingletonTest08 {public static void main(String[] args) {Singleton instance = Singleton.INSTANCE;Singleton instance2 = Singleton.INSTANCE;System.out.println(instance == instance2);System.out.println(instance.hashCode());System.out.println(instance2.hashCode());instance.sayOK();}
} //使用枚举,可以实现单例, 推荐enum Singleton {INSTANCE; //属性public void sayOK() {System.out.println("ok~");}
}
Ø 优缺点说明:
-
这借助 JDK1.5 中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
-
这种方式是 *Effective* *Java* 作者 *Josh* *Bloch* 提倡的方式
结论:推荐使用
二、简单工厂
public class OperationFactory {public static final String OPERATION_ADD = "add"; // 加法运算标识符public static final String OPERATION_SUBTRACT = "subtract"; // 减法运算标识符public static final String OPERATION_MULTIPLY = "multiply"; // 乘法运算标识符public static final String OPERATION_DIVIDE = "divide"; // 除法运算标识符public static Operation createOperation(String operation){Operation mOperation = null;switch (operation){case OPERATION_ADD:mOperation = new OperationAdd(); // 创建加操作运算对象break;case OPERATION_SUBTRACT:mOperation = new OperationSub(); // 创建减操作运算对象break;case OPERATION_MULTIPLY:mOperation = new OperationMul(); // 创建乘操作运算对象break;case OPERATION_DIVIDE:mOperation = new OperationDiv(); // 创建除操作运算对象break;}return mOperation;}
}abstract class Operation {private float numberOne; // 数字1private float numberTwo; // 数字2public float getNumberOne() {return numberOne;}public void setNumberOne(float numberOne) {this.numberOne = numberOne;}public float getNumberTwo() {return numberTwo;}public void setNumberTwo(float numberTwo) {this.numberTwo = numberTwo;}/*** 计算获取结果*/public abstract float getResult();
}
class OperationAdd extends Operation {@Overridepublic float getResult() {return getNumberOne() + getNumberTwo();}
}
class OperationSub extends Operation{@Overridepublic float getResult() {return getNumberOne() - getNumberTwo();}
}class OperationMul extends Operation{@Overridepublic float getResult() {return getNumberOne() * getNumberTwo();}
}
class OperationDiv extends Operation{@Overridepublic float getResult() {float result = 0f;try {result = getNumberOne() / getNumberTwo();} catch (Exception e){e.printStackTrace();}return result;}
}
三、抽象工厂
interface Mainboard {void installCPU();
}
interface Cpu {void calculate();
}
interface AbstractFactory {/*** 创建CPU对象* @return CPU对象*/Cpu createCpu();/*** 创建主板对象* @return 主板对象*/Mainboard createMainboard();
}
class IntelCpu implements Cpu {/*** CPU的针脚数*/private int pins = 0;public IntelCpu(int pins){this.pins = pins;}@Overridepublic void calculate() {System.out.println("Intel CPU的针脚数:" + pins);}
}
class IntelMainboard implements Mainboard {/*** CPU插槽的孔数*/private int cpuHoles = 0;/*** 构造方法,传入CPU插槽的孔数* @param cpuHoles*/public IntelMainboard(int cpuHoles){this.cpuHoles = cpuHoles;}@Overridepublic void installCPU() {System.out.println("Intel主板的CPU插槽孔数是:" + cpuHoles);}
}
class IntelFactory implements AbstractFactory {@Overridepublic Cpu createCpu() {return new IntelCpu(755);}@Overridepublic Mainboard createMainboard() {return new IntelMainboard(755);}
}
class AmdCpu implements Cpu {/*** CPU的针脚数*/private int pins = 0;public AmdCpu(int pins){this.pins = pins;}@Overridepublic void calculate() {System.out.println("Amd CPU的针脚数:" + pins);}
}
class AmdMainboard implements Mainboard {/*** CPU插槽的孔数*/private int cpuHoles = 0;/*** 构造方法,传入CPU插槽的孔数* @param cpuHoles*/public AmdMainboard(int cpuHoles){this.cpuHoles = cpuHoles;}@Overridepublic void installCPU() {System.out.println("Amd主板的CPU插槽孔数是:" + cpuHoles);}
}
class AmdFactory implements AbstractFactory {@Overridepublic Cpu createCpu() {return new AmdCpu(938);}@Overridepublic Mainboard createMainboard() {return new AmdMainboard(938);}
}
class ComputerEngineer {/*** 定义组装机需要的CPU*/private Cpu cpu = null;/*** 定义组装机需要的主板*/private Mainboard mainboard = null;public void makeComputer(AbstractFactory af){/*** 组装机器的基本步骤*///1:首先准备好装机所需要的配件prepareHardwares(af);//2:组装机器//3:测试机器//4:交付客户}private void prepareHardwares(AbstractFactory af){//这里要去准备CPU和主板的具体实现,为了示例简单,这里只准备这两个//可是,装机工程师并不知道如何去创建,怎么办呢?//直接找相应的工厂获取this.cpu = af.createCpu();this.mainboard = af.createMainboard();//测试配件是否好用this.cpu.calculate();this.mainboard.installCPU();}
}
class Client {public static void main(String[]args){//创建装机工程师对象ComputerEngineer cf = new ComputerEngineer();//客户选择并创建需要使用的产品对象AbstractFactory af = new IntelFactory();//告诉装机工程师自己选择的产品,让装机工程师组装电脑cf.makeComputer(af);cf.makeComputer(new AmdFactory());}
}
四、原型模式
-
原型模式(Prototype 模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象
-
原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节
-
工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即 对象**.clone()**
public class Sheep implements Cloneable {private String name;private int age;private String color;private String address = "蒙古羊";public Sheep friend; //是对象, 克隆是会如何处理, 默认是浅拷贝public Sheep(String name, int age, String color) {super();this.name = name;this.age = age;this.color = color;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}@Overridepublic String toString() {return "Sheep [name=" + name + ", age=" + age + ", color=" + color + ", address=" + address + "]";}//克隆该实例,使用默认的 clone 方法来完成@Overrideprotected Object clone() {Sheep sheep = null;try {sheep = (Sheep) super.clone();//sheep.setFriend((Sheep) this.friend.clone());实现深拷贝} catch (Exception e) {}return sheep;}
}class Client {public static void main(String[] args) {System.out.println("原型模式完成对象的创建");Sheep sheep = new Sheep("tom", 1, "白色");sheep.friend = new Sheep("jack", 2, "黑色");Sheep sheep2 = (Sheep) sheep.clone(); //克隆Sheep sheep3 = (Sheep) sheep.clone();Sheep sheep4 = (Sheep) sheep.clone();Sheep sheep5 = (Sheep) sheep.clone(); //克隆System.out.println("sheep2 =" + sheep2 + "sheep2.friend=" + sheep2.friend.hashCode());System.out.println("sheep3 =" + sheep3 + "sheep3.friend=" + sheep3.friend.hashCode());System.out.println("sheep4 =" + sheep4 + "sheep4.friend=" + sheep4.friend.hashCode());System.out.println("sheep5 =" + sheep5 + "sheep5.friend=" + sheep5.friend.hashCode());}
}
浅拷贝的介绍
-
对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
-
对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值
-
前面我们克隆羊就是浅拷贝
-
浅拷贝是使用默认的 clone()方法来实现
sheep = (Sheep) super.clone();
深拷贝基本介绍
-
复制对象的所有基本数据类型的成员变量值
-
为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象**(包括对象的引用类型)**进行拷贝
-
深拷贝实现方式 1:重写 clone 方法来实现深拷贝
-
深拷贝实现方式 2:通过对象序列化实现深拷贝(推荐)
//一、所有对象都实现深拷贝
public class Exam {public static void main(String[] args) throws CloneNotSupportedException {// 创建被赋值对象Address address = new Address(110, "北京");People p1 = new People(1, "Java", address);// 克隆 p1 对象People p2 = p1.clone();// 修改原型对象p1.getAddress().setCity("西安");// 输出 p1 和 p2 地址信息System.out.println("p1:" + p1.getAddress().getCity() +" p2:" + p2.getAddress().getCity());System.out.println(p1.hashCode()==p2.hashCode());//false}/*** 用户类*/@Data@AllArgsConstructorstatic class People implements Cloneable {private Integer id;private String name;private Address address;/*** 重写 clone 方法* @throws CloneNotSupportedException*/@Overrideprotected People clone() throws CloneNotSupportedException {People people = (People) super.clone();people.setAddress(this.address.clone()); // 引用类型克隆赋值return people;}// 忽略构造方法、set、get 方法}/*** 地址类*/@Data@AllArgsConstructorstatic class Address implements Cloneable {private Integer id;private String city;/*** 重写 clone 方法* @throws CloneNotSupportedException*/@Overrideprotected Address clone() throws CloneNotSupportedException {return (Address) super.clone();}// 忽略构造方法、set、get 方法}
}
//方式二:通过字节流实现深拷贝
public class Exam {public static void main(String[] args) throws CloneNotSupportedException {// 创建对象Address address = new Address(110, "北京");People p1 = new People(1, "Java", address);// 通过字节流实现克隆People p2 = (People) StreamClone.clone(p1);// 修改原型对象p1.getAddress().setCity("西安");// 输出 p1 和 p2 地址信息System.out.println("p1:" + p1.getAddress().getCity() +" p2:" + p2.getAddress().getCity());System.out.println(p1.hashCode()==p2.hashCode());//false}/*** 通过字节流实现克隆*/static class StreamClone {public static <T extends Serializable> T clone(People obj) {T cloneObj = null;try {// 写入字节流ByteArrayOutputStream bo = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bo);oos.writeObject(obj);oos.close();// 分配内存,写入原始对象,生成新对象ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());//获取上面的输出字节流ObjectInputStream oi = new ObjectInputStream(bi);// 返回生成的新对象cloneObj = (T) oi.readObject();oi.close();} catch (Exception e) {e.printStackTrace();}return cloneObj;}}/*** 用户类*/@AllArgsConstructor@Datastatic class People implements Serializable {private Integer id;private String name;private Address address;// 忽略构造方法、set、get 方法}/*** 地址类*/@AllArgsConstructor@Datastatic class Address implements Serializable {private Integer id;private String city;// 忽略构造方法、set、get 方法}
}
//三、构造器实现深拷贝
public class Sheep {public static void main(String[] args) throws CloneNotSupportedException {// 创建对象Address address = new Address(110, "北京");People p1 = new People(1, "Java", address);// 调用构造函数克隆对象People p2 = new People(p1.getId(), p1.getName(),new Address(p1.getAddress().getId(), p1.getAddress().getCity()));// 修改原型对象p1.getAddress().setCity("西安");// 输出 p1 和 p2 地址信息System.out.println("p1:" + p1.getAddress().getCity() +" p2:" + p2.getAddress().getCity());System.out.println(p1.hashCode()==p2.hashCode());}/*** 用户类*/@Data@AllArgsConstructorstatic class People {private Integer id;private String name;private Address address;// 忽略构造方法、set、get 方法}/*** 地址类*/@Data@AllArgsConstructorstatic class Address {private Integer id;private String city;// 忽略构造方法、set、get 方法}
}
/*** 深克隆实现方式四:通过 apache.commons.lang 实现*/
public class Sheep {public static void main(String[] args) throws CloneNotSupportedException {// 创建对象Address address = new Address(110, "北京");People p1 = new People(1, "Java", address);// 调用 apache.commons.lang 克隆对象People p2 = (People) SerializationUtils.clone(p1);// 修改原型对象p1.getAddress().setCity("西安");// 输出 p1 和 p2 地址信息System.out.println("p1:" + p1.getAddress().getCity() +" p2:" + p2.getAddress().getCity());}/*** 用户类*/@AllArgsConstructor@Datastatic class People implements Serializable {private Integer id;private String name;private Address address;// 忽略构造方法、set、get 方法}/*** 地址类*/@AllArgsConstructor@Datastatic class Address implements Serializable {private Integer id;private String city;// 忽略构造方法、set、get 方法}
}
原型模式的注意事项和细节
-
创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率
-
不用重新初始化对象,而是动态地获得对象运行时的状态
-
如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码
-
在实现深克隆的时候可能需要比较复杂的代码
-
缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了 ocp 原则
五、建造者模式
建造者模式基本介绍
-
建造者模式(Builder Pattern) 又叫生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。
-
建造者模式 是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们, 用户不需要知道内部的具体构建细节。
建造者模式的四个角色
-
Product(产品角色): 一个具体的产品对象。
-
Builder(抽象建造者): 创建一个 Product 对象的各个部件指定的 接口/抽象类。
-
ConcreteBuilder(具体建造者): 实现接口,构建和装配各个部件。
-
Director(指挥者): 构建一个使用 Builder 接口的对象。它主要是用于创建一个复杂的对象。它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程。
public class Client {public static void main(String[] args) {//盖普通房子CommonHouse commonHouse = new CommonHouse();//准备创建房子的指挥者HouseDirector houseDirector = new HouseDirector(commonHouse);//完成盖房子,返回产品(普通房子)House house = houseDirector.constructHouse();System.out.println(" 输 出 流 程 ");System.out.println("--------------------------");//盖高楼HighBuilding highBuilding = new HighBuilding();//重置建造者houseDirector.setHouseBuilder(highBuilding);//完成盖房子,返回产品(高楼)houseDirector.constructHouse();}
}class CommonHouse extends HouseBuilder {@Overridepublic void buildBasic() {System.out.println(" 普通房子打地基 5 米 ");}@Overridepublic void buildWalls() {System.out.println(" 普通房子砌墙 10cm ");}@Overridepublic void roofed() {System.out.println(" 普通房子屋顶 ");}
}class HighBuilding extends HouseBuilder {@Overridepublic void buildBasic() {System.out.println(" 高楼的打地基 100 米 ");}@Overridepublic void buildWalls() {System.out.println(" 高楼的砌墙 20cm ");}@Overridepublic void roofed() {System.out.println(" 高楼的透明屋顶 ");}
}// 产 品 ->Product
class House {private String baise;private String wall;private String roofed;public String getBaise() {return baise;}public void setBaise(String baise) {this.baise = baise;}public String getWall() {return wall;}public void setWall(String wall) {this.wall = wall;}public String getRoofed() {return roofed;}public void setRoofed(String roofed) {this.roofed = roofed;}
}
// 抽象的建造者
abstract class HouseBuilder {protected House house = new House();//将建造的流程写好, 抽象的方法public abstract void buildBasic();public abstract void buildWalls();public abstract void roofed();//建造房子好, 将产品(房子) 返回public House buildHouse() {return house;}
}//指挥者,这里去指定制作流程,返回产品
class HouseDirector {HouseBuilder houseBuilder = null;//构造器传入 houseBuilderpublic HouseDirector(HouseBuilder houseBuilder) {this.houseBuilder = houseBuilder;}//通过 setter 传入 houseBuilderpublic void setHouseBuilder(HouseBuilder houseBuilder) {this.houseBuilder = houseBuilder;}//如何处理建造房子的流程,交给指挥者public House constructHouse() {houseBuilder.buildBasic();houseBuilder.buildWalls();houseBuilder.roofed();return houseBuilder.buildHouse();}
}
六、适配器模式
适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)
类适配器模式
public class Client {public static void main(String[] args) {System.out.println(" === 类适配器模式 ====");Phone phone = new Phone();phone.charging(new VoltageAdapter());}
}//适配接口
interface IVoltage5V {public int output5V();
}class Phone {//充电public void charging(IVoltage5V iVoltage5V) {if (iVoltage5V.output5V() == 5) {System.out.println("电压为 5V, 可以充电~~");} else if (iVoltage5V.output5V() > 5) {System.out.println("电压大于 5V, 不能充电~~");}}
}//被适配的类
class Voltage220V {//输出 220V 的电压public int output220V() {int src = 220;System.out.println("电压=" + src + "伏");return src;}
}//适配器类
class VoltageAdapter extends Voltage220V implements IVoltage5V {@Overridepublic int output5V() {
// TODO Auto-generated method stub
//获取到 220V 电压int srcV = output220V();int dstV = srcV / 44;return dstV;}
}
对象适配器
public class Client {public static void main(String[] args) {System.out.println(" === 对象适配器模式 ====");Phone phone = new Phone();phone.charging(new VoltageAdapter(new Voltage220V()));}}//适配接口
interface IVoltage5V {public int output5V();
}class Phone {//充电public void charging(IVoltage5V iVoltage5V) {if (iVoltage5V.output5V() == 5) {System.out.println("电压为 5V, 可以充电~~");} else if (iVoltage5V.output5V() > 5) {System.out.println("电压大于 5V, 不能充电~~");}}
}//被适配的类
class Voltage220V {//输出 220V 的电压,不变public int output220V() {int src = 220;System.out.println("电压=" + src + "伏");return src;}
}//适配器类
class VoltageAdapter implements IVoltage5V {private Voltage220V voltage220V; // 关联关系-聚合//通过构造器,传入一个 Voltage220V 实例public VoltageAdapter(Voltage220V voltage220v) {this.voltage220V = voltage220v;}@Overridepublic int output5V() {int dst = 0;if (null != voltage220V) {int src = voltage220V.output220V();//获取 220V 电压System.out.println("使用对象适配器,进行适配~~");dst = src / 44;System.out.println("适配完成,输出的电压为=" + dst);}return dst;}
}
接口适配器
interface Interface4 {public void m1();public void m2();public void m3();public void m4();
}
//在 AbsAdapter 我们将 Interface4 的方法进行默认实现
abstract class AbsAdapter implements Interface4 {//默认实现@Overridepublic void m1() {}@Overridepublic void m2() {}@Overridepublic void m3() {}@Overridepublic void m4() {}
}
class Client {public static void main(String[] args) {AbsAdapter absAdapter = new AbsAdapter() {//只需要去覆盖我们 需要使用 接口方法@Overridepublic void m1() {System.out.println("使用了 m1 的方法");}};absAdapter.m1();}
}
七、桥接模式
基本介绍
-
桥接模式(Bridge 模式)是指:将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变。
-
是一种结构型设计模式
-
Bridge 模式基于类的最小设计原则,通过使用封装、聚合及继承等行为让不同的类承担不同的职责。它的主要特点是把抽象(Abstraction)与行为实现(Implementation)分离开来,从而可以保持各部分的独立性以及应对他们的功能扩展
class Client {public static void main(String[] args) {//获取折叠式手机 (样式 + 品牌 )Phone phone1 = new FoldedPhone(new XiaoMi());phone1.open();phone1.call();phone1.close();System.out.println("=======================");Phone phone2 = new FoldedPhone(new Vivo());phone2.open();phone2.call();phone2.close();System.out.println("==============");UpRightPhone phone3 = new UpRightPhone(new XiaoMi());phone3.open();phone3.call();phone3.close();System.out.println("==============");UpRightPhone phone4 = new UpRightPhone(new Vivo());phone4.open();phone4.call();phone4.close();}
}
//接口
interface Brand {void open();void close();void call();
}
//折叠式手机类,继承 抽象类 Phone
class FoldedPhone extends Phone {//构造器public FoldedPhone(Brand brand) {super(brand);}@Overridepublic void open() {super.open();System.out.println(" 折叠样式手机 ");}@Overridepublic void close() {super.close();System.out.println(" 折叠样式手机 ");}@Overridepublic void call() {super.call();System.out.println(" 折叠样式手机 ");}
}abstract class Phone {//组合品牌private Brand brand;//构造器public Phone(Brand brand) {super();this.brand = brand;}protected void open() {this.brand.open();}protected void close() {brand.close();}protected void call() {brand.call();}}class UpRightPhone extends Phone {//构造器public UpRightPhone(Brand brand) {super(brand);}@Overridepublic void open() {super.open();System.out.println(" 直立样式手机 ");}@Overridepublic void close() {super.close();System.out.println(" 直立样式手机 ");}public void call() {super.call();System.out.println(" 直立样式手机 ");}
}class Vivo implements Brand {@Overridepublic void open() {System.out.println(" Vivo 手机开机 ");}@Overridepublic void close() {System.out.println(" Vivo 手机关机 ");}@Overridepublic void call() {System.out.println(" Vivo 手机打电话 ");}
}class XiaoMi implements Brand {@Overridepublic void open() {System.out.println(" 小米手机开机 ");}@Overridepublic void close() {System.out.println(" 小米手机关机 ");}@Overridepublic void call() {System.out.println(" 小米手机打电话 ");}
}
桥接模式的注意事项和细节
-
实现了抽象和实现部分的分离,从而极大的提供了系统的灵活性,让抽象部分和实现部分独立开来,这有助于系统进行分层设计,从而产生更好的结构化系统。
-
对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了,其它的部分由具体业务来完成。
-
桥接模式替代多层继承方案,可以减少子类的个数,降低系统的管理和维护成本。
-
桥接模式的引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计和编程
桥接模式要求正确识别出系统中两个独立变化的维度(抽象、和实现),因此其使用范围有一定的局限性,即需要有这样的应用场景
桥接模式在JDBC中的应用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WwHpAORO-1598954057039)(C:\Users\lss\AppData\Roaming\Typora\typora-user-images\image-20200802132341114.png)]
八、装饰器模式
基本介绍
装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则**(ocp)**
没搞太懂
//具体的 Decorator, 这里就是调味品class CoffeeBar {public static void main(String[] args) {// 装饰者模式下的订单:2 份巧克力+一份牛奶的 LongBlack// 1. 点一份 LongBlackDrink order = new LongBlack();System.out.println("费用 1=" + order.cost());System.out.println("描述=" + order.getDes());// 2. order 加入一份牛奶order = new Milk(order);System.out.println("order 加入一份牛奶 费用 =" + order.cost());System.out.println("order 加入一份牛奶 描述 = " + order.getDes());// 3. order 加入一份巧克力order = new Chocolate(order);System.out.println("order 加入一份牛奶 加入一份巧克力 费 用 =" + order.cost());System.out.println("order 加入一份牛奶 加入一份巧克力 描述 = " + order.getDes());// 3. order 加入一份巧克力order = new Chocolate(order);System.out.println("order 加入一份牛奶 加入 2 份巧克力 费 用 =" + order.cost());System.out.println("order 加入一份牛奶 加入 2 份巧克力 描述 = " + order.getDes());System.out.println("===========================");Drink order2 = new DeCaf();System.out.println("order2 无因咖啡 费 用 =" + order2.cost());System.out.println("order2 无因咖啡 描述 = " + order2.getDes());order2 = new Milk(order2);System.out.println("order2 无因咖啡 加入一份牛奶 费 用 =" + order2.cost());System.out.println("order2 无因咖啡 加入一份牛奶 描述 = " + order2.getDes());}
}class Decorator extends Drink {private Drink obj;public Decorator(Drink obj) { //组合this.obj = obj;}@Overridepublic float cost() {// getPrice 自己价格return super.getPrice() + obj.cost();}@Overridepublic String getDes() {// obj.getDes() 输出被装饰者的信息return des + " " + getPrice() + " && " + obj.getDes();}
}abstract class Drink {public String des; // 描 述private float price = 0.0f;public String getDes() {return des;}public void setDes(String des) {this.des = des;}public float getPrice() {return price;}public void setPrice(float price) {this.price = price;}//计算费用的抽象方法//子类来实现public abstract float cost();}class Espresso extends Coffee {public Espresso() {setDes(" 意大利咖啡 ");setPrice(6.0f);}
}class LongBlack extends Coffee {public LongBlack() {setDes(" longblack ");setPrice(5.0f);}
}class Milk extends Decorator {public Milk(Drink obj) {super(obj);setDes(" 牛 奶 ");setPrice(2.0f);}
}class ShortBlack extends Coffee {public ShortBlack() {setDes(" shortblack ");setPrice(4.0f);}
}class Soy extends Decorator {public Soy(Drink obj) {super(obj);setDes(" 豆浆 ");setPrice(1.5f);}
}class Chocolate extends Decorator {public Chocolate(Drink obj) {super(obj);setDes(" 巧克力 ");setPrice(3.0f); // 调味品 的价格}
}class Coffee extends Drink {@Overridepublic float cost() {return super.getPrice();}
}class DeCaf extends Coffee {public DeCaf() {setDes(" 无因咖啡 ");setPrice(1.0f);}
}
/**
费用 1=5.0
描述= longblack
order 加入一份牛奶 费用 =7.0
order 加入一份牛奶 描述 = 牛 奶 2.0 && longblack
order 加入一份牛奶 加入一份巧克力 费 用 =10.0
order 加入一份牛奶 加入一份巧克力 描述 = 巧克力 3.0 && 牛 奶 2.0 && longblack
order 加入一份牛奶 加入 2 份巧克力 费 用 =13.0
order 加入一份牛奶 加入 2 份巧克力 描述 = 巧克力 3.0 && 巧克力 3.0 && 牛 奶 2.0 && longblack
===========================
order2 无因咖啡 费 用 =1.0
order2 无因咖啡 描述 = 无因咖啡
order2 无因咖啡 加入一份牛奶 费 用 =3.0
order2 无因咖啡 加入一份牛奶 描述 = 牛 奶 2.0 && 无因咖啡
**/
九、组合模式
基本介绍
-
组合模式(Composite Pattern),又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以表示“整体****-****部分”的层次关系。
-
组合模式依据树形结构来组合对象,用来表示部分以及整体层次。
-
这种类型的设计模式属于结构型模式。
-
组合模式使得用户对单个对象和组合对象的访问具有一致性,即:组合能让客户以一致的方式处理个别对象以及组合对象
类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nBUyJ9Ez-1598954057044)(C:\Users\lss\AppData\Roaming\Typora\typora-user-images\image-20200802171415596.png)]
package com.code.mynettypro.desigin;import java.util.ArrayList;
import java.util.List;public class Client {public static void main(String[] args) {//从大到小创建对象 学校OrganizationComponent university = new University("清华大学", " 中国顶级大学 ");//创建 学院OrganizationComponent computerCollege = new College(" 计 算 机 学 院 ", " 计 算 机 学 院 ");OrganizationComponent infoEngineercollege = new College("信息工程学院", " 信息工程学院 ");//创建各个学院下面的系(专业)computerCollege.add(new Department("软件工程", " 软件工程不错 "));computerCollege.add(new Department("网络工程", " 网络工程不错 "));computerCollege.add(new Department("计算机科学与技术", " 计算机科学与技术是老牌的专业 "));infoEngineercollege.add(new Department("通信工程", " 通信工程不好学 "));infoEngineercollege.add(new Department("信息工程", " 信息工程好学 "));//将学院加入到 学校university.add(computerCollege);university.add(infoEngineercollege);university.print(); infoEngineercollege.print();}}class College extends OrganizationComponent {//List 中 存放的 DepartmentList<OrganizationComponent> organizationComponents = new ArrayList<OrganizationComponent>();// 构造器public College(String name, String des) {super(name, des);}// 重 写 add @Overrideprotected void add(OrganizationComponent organizationComponent) {
// 将来实际业务中,Colleage 的 add 和 University add 不一定完全一样organizationComponents.add(organizationComponent);}// 重 写 remove @Overrideprotected void remove(OrganizationComponent organizationComponent) {organizationComponents.remove(organizationComponent);}@Overridepublic String getName() {return super.getName();}@Overridepublic String getDes() {return super.getDes();}// print 方法,就是输出 University 包含的学院@Overrideprotected void print() {System.out.println("--------------" + getName() + "--------------");//遍历 organizationComponentsfor (OrganizationComponent organizationComponent : organizationComponents) {organizationComponent.print();}}
}class Department extends OrganizationComponent {//没有集合public Department(String name, String des) {super(name, des);}//add , remove 就不用写了,因为他是叶子节点@Overridepublic String getName() {return super.getName();}@Overridepublic String getDes() {return super.getDes();}@Overrideprotected void print() {System.out.println(getName());}
}abstract class OrganizationComponent {private String name; // 名 字private String des; // 说 明protected void add(OrganizationComponent organizationComponent) {//默认实现throw new UnsupportedOperationException();}protected void remove(OrganizationComponent organizationComponent) {//默认实现throw new UnsupportedOperationException();}//构造器public OrganizationComponent(String name, String des) {super();this.name = name;this.des = des;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getDes() {return des;}public void setDes(String des) {this.des = des;}//方法 print, 做成抽象的, 子类都需要实现protected abstract void print();
}//University 就是 Composite , 可以管理 College
class University extends OrganizationComponent {List<OrganizationComponent> organizationComponents = new ArrayList<>();// 构造器public University(String name, String des) {super(name, des);}// 重 写 add @Overrideprotected void add(OrganizationComponent organizationComponent) {organizationComponents.add(organizationComponent);}// 重 写 remove @Overrideprotected void remove(OrganizationComponent organizationComponent) {organizationComponents.remove(organizationComponent);}@Overridepublic String getName() {return super.getName();}@Overridepublic String getDes() {return super.getDes();}// print 方法,就是输出 University 包含的学院@Overrideprotected void print() {System.out.println("--------------" + getName() + "--------------");//遍历 organizationComponentsfor (OrganizationComponent organizationComponent : organizationComponents) {organizationComponent.print();}}
}
/**
--------------清华大学--------------
-------------- 计 算 机 学 院 --------------
软件工程
网络工程
计算机科学与技术
--------------信息工程学院--------------
通信工程
信息工程
--------------信息工程学院--------------
通信工程
信息工程
**/
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fffSyEru-1598954057049)(C:\Users\lss\AppData\Roaming\Typora\typora-user-images\image-20200802170131967.png)]
十、外观模式
外观模式基本介绍
-
外观模式(Facade),也叫“过程模式:外观模式为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用
-
外观模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节
public class Client {public static void main(String[] args) {//这里直接调用。。 很麻烦HomeTheaterFacade homeTheaterFacade = new HomeTheaterFacade();homeTheaterFacade.ready();homeTheaterFacade.play();homeTheaterFacade.end();}
}class DVDPlayer {//使用单例模式, 使用饿汉式private static DVDPlayer instance = new DVDPlayer();public static DVDPlayer getInstanc() {return instance;}public void on() {System.out.println(" dvd on ");}public void off() {System.out.println(" dvd off ");}public void play() {System.out.println(" dvd is playing ");}//....public void pause() {System.out.println(" dvd pause ..");}
}class HomeTheaterFacade {//定义各个子系统对象private TheaterLight theaterLight;private Popcorn popcorn;private Stereo stereo;private Projector projector;private Screen screen;private DVDPlayer dVDPlayer;//构造器public HomeTheaterFacade() {super();this.theaterLight = TheaterLight.getInstance();this.popcorn = Popcorn.getInstance();this.stereo = Stereo.getInstance();this.projector = Projector.getInstance();this.screen = Screen.getInstance();this.dVDPlayer = DVDPlayer.getInstanc();}//操作分成 4 步public void ready() {popcorn.on();popcorn.pop();screen.down();projector.on();stereo.on();dVDPlayer.on();theaterLight.dim();}public void play() {dVDPlayer.play();}public void pause() {dVDPlayer.pause();}public void end() {popcorn.off();theaterLight.bright();screen.up();projector.off();stereo.off();dVDPlayer.off();}
}class Popcorn {private static Popcorn instance = new Popcorn();public static Popcorn getInstance() {return instance;}public void on() {System.out.println(" popcorn on ");}public void off() {System.out.println(" popcorn ff ");}public void pop() {System.out.println(" popcorn is poping ");}
}class Projector {private static Projector instance = new Projector();public static Projector getInstance() {return instance;}public void on() {System.out.println(" Projector on ");}public void off() {System.out.println(" Projector ff ");}public void focus() {System.out.println(" Projector is Projector ");}}class Screen {private static Screen instance = new Screen();public static Screen getInstance() {return instance;}public void up() {System.out.println(" Screen up ");}public void down() {System.out.println(" Screen down ");}}class Stereo {private static Stereo instance = new Stereo();public static Stereo getInstance() {return instance;}public void on() {System.out.println(" Stereo on ");}public void off() {System.out.println(" Screen off ");}public void up() {System.out.println(" Screen up.. ");}//...
}class TheaterLight {private static TheaterLight instance = new TheaterLight();public static TheaterLight getInstance() {return instance;}public void on() {System.out.println(" TheaterLight on ");}public void off() {System.out.println(" TheaterLight off ");}public void dim() {System.out.println(" TheaterLight dim.. ");}public void bright() {System.out.println(" TheaterLight bright.. ");}
}
外观模式在mybatis中的应用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5aOSP2g0-1598954057053)(C:\Users\lss\AppData\Roaming\Typora\typora-user-images\image-20200802193905300.png)]
外观模式的注意事项和细节
-
外观模式对外屏蔽了子系统的细节,因此外观模式降低了客户端对子系统使用的复杂性
-
外观模式对客户端与子系统的耦合关系 - 解耦,让子系统内部的模块更易维护和扩展
-
通过合理的使用外观模式,可以帮我们更好的划分访问的层次
-
当系统需要进行分层设计时,可以考虑使用 Facade 模式
-
在维护一个遗留的大型系统时,可能这个系统已经变得非常难以维护和扩展,此时可以考虑为新系统开发一个
Facade 类,来提供遗留系统的比较清晰简单的接口,让新系统与 Facade 类交互,提高复用性
不能过多的或者不合理的使用外观模式,使用外观模式好,还是直接调用模块好。要以让系统有层次,利于维护为目的
十一、享元模式
基本介绍
-
享元模式(Flyweight Pattern) 也叫 蝇量模式: 运用共享技术有效地支持大量细粒度的对象
-
常用于系统底层开发,解决系统的性能问题。像数据库连接池,里面都是创建好的连接对象,在这些连接对象中有我们需要的则直接拿来用,避免重新创建,如果没有我们需要的,则创建一个
-
享元模式能够解决重复对象的内存浪费的问题,当系统中有大量相似对象,需要缓冲池时。不需总是创建新对象,可以从缓冲池里拿。这样可以降低系统内存,同时提高效率
-
享元模式经典的应用场景就是池技术了,String 常量池、数据库连接池、缓冲池等等都是享元模式的应用,享元模式是池技术的重要实现方式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xi0N8few-1598954057054)(file:///C:\Users\lss\AppData\Local\Temp\ksohtml7808\wps1.png)]
类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rELS3S9D-1598954057056)(C:\Users\lss\AppData\Roaming\Typora\typora-user-images\image-20200802203424544.png)]
import java.util.HashMap;public class Client {public static void main(String[] args) {// 创建一个工厂类WebSiteFactory factory = new WebSiteFactory();// 客户要一个以新闻形式发布的网站WebSite webSite1 = factory.getWebSiteCategory("新闻");webSite1.use(new User("tom"));// 客户要一个以博客形式发布的网站WebSite webSite2 = factory.getWebSiteCategory("博客");webSite2.use(new User("jack"));// 客户要一个以博客形式发布的网站WebSite webSite3 = factory.getWebSiteCategory("博客");webSite3.use(new User("smith"));// 客户要一个以博客形式发布的网站WebSite webSite4 = factory.getWebSiteCategory("博客");webSite4.use(new User("king"));System.out.println("网站的分类共=" + factory.getWebSiteCount());}}//具体网站
class ConcreteWebSite extends WebSite {//共享的部分,内部状态private String type = ""; //网站发布的形式(类型)//构造器public ConcreteWebSite(String type) {this.type = type;}@Overridepublic void use(User user) {System.out.println("网站的发布形式为:" + type + " 在使用中 .. 使用者是" + user.getName());}
}class User {private String name;public User(String name) {super();this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}}abstract class WebSite {public abstract void use(User user);//抽象方法
}// 网站工厂类,根据需要返回压一个网站
class WebSiteFactory {//集合, 充当池的作用private HashMap<String, ConcreteWebSite> pool = new HashMap<>();//根据网站的类型,返回一个网站, 如果没有就创建一个网站,并放入到池中,并返回public WebSite getWebSiteCategory(String type) {if (!pool.containsKey(type)) {//就创建一个网站,并放入到池中pool.put(type, new ConcreteWebSite(type));}return (WebSite) pool.get(type);}//获取网站分类的总数 (池中有多少个网站类型)public int getWebSiteCount() {return pool.size();}
}
享元模式在 JDK-Interger 的应用源码分析
-
Integer 中的享元模式
-
代码分析+Debug 源码+说明
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9JXOKtUZ-1598954057073)(file:///C:\Users\lss\AppData\Local\Temp\ksohtml7808\wps2.png)]
public class Client {public static void main(String[] args) {
//如果 Integer.valueOf(x) x 在 -128 --- 127 直接,就是使用享元模式返回,如果不在 范围类,则仍然 new//小结:
//1. 在 valueOf 方法中,先判断值是否在 IntegerCache 中,如果不在,就创建新的 Integer(new), 否则,就直接从 缓存池返回
//2. valueOf 方法,就使用到享元模式
//3. 如果使用 valueOf 方法得到一个 Integer 实例,范围在 -128 - 127 ,执行速度比 new 快 Integer x = Integer.valueOf(127); // 得到 x 实例,类型 IntegerInteger y = new Integer(127); // 得 到 y 实 例 , 类 型 IntegerInteger z = Integer.valueOf(127);//..Integer w = new Integer(127);System.out.println(x.equals(y)); // 大小,trueSystem.out.println(x == y); // falseSystem.out.println(x == z); // trueSystem.out.println(w == x); // falseSystem.out.println(w == y); // falseInteger x1 = Integer.valueOf(200);Integer x2 = Integer.valueOf(200);System.out.println("x1==x2" + (x1 == x2)); // false}}
十二、代理模式
代理模式的基本介绍
-
代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
-
被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象
-
代理模式有不同的形式, 主要有三种 静态代理、动态代理 (JDK 代理、接口代理)和 Cglib代理 (可以在内存动态的创建对象,而不需要实现接口, 他是属于动态代理的范畴) 。
-
代理模式示意图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SQ6Du1ya-1598954057075)(file:///C:\Users\lisensen\AppData\Local\Temp\ksohtml\wps7D5D.tmp.png)]
1、静态代理模式的基本介绍
静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类
public class Client {public static void main(String[] args) {//创建目标对象(被代理对象)TeacherDao teacherDao = new TeacherDao();//创建代理对象, 同时将被代理对象传递给代理对象TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao);//通过代理对象,调用到被代理对象的方法//即:执行的是代理对象的方法,代理对象再去调用目标对象的方法teacherDaoProxy.teach();}
}
//接口
public interface ITeacherDao {void teach(); // 授课的方法
}
public class TeacherDao implements ITeacherDao {@Overridepublic void teach() {System.out.println(" 老师授课中 。。。。。");}
}
//代理对象,静态代理
public class TeacherDaoProxy implements ITeacherDao {// 目标对象,通过接口来聚合private ITeacherDao target;//构造器public TeacherDaoProxy(ITeacherDao target) {this.target = target;}@Overridepublic void teach() {System.out.println("开始代理 完成某些操作。。。。。 ");System.out.println("提交。。。。。");}
}
/**
开始代理 完成某些操作。。。。。
提交。。。。。
**/
静态代理优缺点
-
优点:在不修改目标对象的功能前提下, 能通过代理对象对目标功能扩展
-
缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类
-
一旦接口增加方法,目标对象与代理对象都要维护
2、动态代理模式的基本介绍
-
代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
-
代理对象的生成,是利用 JDK 的 API,动态的在内存中构建代理对象
-
动态代理也叫做:JDK 代理、接口代理
public class Client {public static void main(String[] args) {//创建目标对象ITeacherDao target = new TeacherDao();//给目标对象,创建代理对象, 可以转成 ITeacherDaoITeacherDao proxyInstance = (ITeacherDao) new ProxyFactory(target).getProxyInstance();// proxyInstance=class com.sun.proxy.$Proxy0 内存中动态生成了代理对象System.out.println("proxyInstance=" + proxyInstance.getClass());//通过代理对象,调用目标对象的方法//proxyInstance.teach();proxyInstance.sayHello(" tom ");}}
//接口
public interface ITeacherDao {void teach(); // 授课方法void sayHello(String name);
}
public class TeacherDao implements ITeacherDao {@Overridepublic void teach() {System.out.println(" 老师授课中.... ");}@Overridepublic void sayHello(String name) {System.out.println("hello " + name);}
}import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class ProxyFactory {//维护一个目标对象 , Objectprivate Object target;//构造器 , 对 target 进行初始化public ProxyFactory(Object target) {this.target = target;}//给目标对象 生成一个代理对象public Object getProxyInstance() {//说明/** public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)//1. ClassLoader loader : 指定当前目标对象使用的类加载器, 获取加载器的方法固定//2. Class<?>[] interfaces: 目标对象实现的接口类型,使用泛型方法确认类型//3. InvocationHandler h : 事情处理,执行目标对象的方法时,会触发事情处理器方法, 会把当前执行的目标对象方法作为参数传入*/return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("JDK 代理开始~~");//反射机制调用目标对象的方法Object returnVal = method.invoke(target, args);System.out.println("JDK 代理提交");return returnVal;}});}
}
3、Cglib 代理模式的基本介绍
-
静态代理和 JDK 代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理-这就是 Cglib 代理
-
Cglib 代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展, 有些书也将Cglib 代理归属到动态代理。
-
Cglib 是一个强大的高性能的代码生成包,它可以在运行期扩展 java 类与实现 java 接口.它广泛的被许多 AOP 的框架使用,例如 Spring AOP,实现方法拦截
-
在 AOP 编程中如何选择代理模式:
-
目标对象需要实现接口,用 JDK 代理
-
目标对象不需要实现接口,用 Cglib 代理
Cglib 包的底层是通过使用字节码处理框架 ASM 来转换字节码并生成新的类
Cglib 代理模式实现步骤
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GPyTBJKK-1598954057078)(file:///C:\Users\lisensen\AppData\Local\Temp\ksohtml\wps810E.tmp.png)] |
需要引入 cglib 的 jar 文件
-
在内存中动态构建子类,注意代理的类不能为 final,否则报错java.lang.IllegalArgumentException:
-
目标对象的方法如果为 final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.
public class TeacherDao {public String teach() {System.out.println(" 老师授课中 , 我是 cglib 代理,不需要实现接口 ");return "hello";}
}import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;public class ProxyFactory implements MethodInterceptor {//维护一个目标对象private Object target;//构造器,传入一个被代理的对象public ProxyFactory(Object target) {this.target = target;}//返回一个代理对象: 是 target 对象的代理对象public Object getProxyInstance() {//1. 创建一个工具类Enhancer enhancer = new Enhancer();//2. 设置父类enhancer.setSuperclass(target.getClass());//3. 设置回调函数enhancer.setCallback(this);//4. 创建子类对象,即代理对象return enhancer.create();}//重写 intercept 方法,会调用目标对象的方法@Overridepublic Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable {System.out.println("Cglib 代理模式 ~~ 开始");Object returnVal = method.invoke(target, args);System.out.println("Cglib 代理模式 ~~ 提交");return returnVal;}
}public class Client {public static void main(String[] args) {//创建目标对象TeacherDao target = new TeacherDao();//获取到代理对象,并且将目标对象传递给代理对象TeacherDao proxyInstance = (TeacherDao) new ProxyFactory(target).getProxyInstance();//执行代理对象的方法,触发 intecept 方法,从而实现 对目标对象的调用String res = proxyInstance.teach();System.out.println("res=" + res);}}
/**
Cglib 代理模式 ~~ 开始老师授课中 , 我是 cglib 代理,不需要实现接口
Cglib 代理模式 ~~ 提交
res=hello
**/
十三、模板方法模式
模板方法模式基本介绍
-
模板方法模式(Template Method Pattern),又叫模板模式(Template Pattern),在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
-
简单说,模板方法模式 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤
这种类型的设计模式属于行为型模式
模板方法模式的原理类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fvLo7VxM-1598954057081)(C:\Users\lisensen\AppData\Roaming\Typora\typora-user-images\image-20200803162828850.png)]
Ø 对原理类图的说明-即(模板方法模式的角色及职责)
-
AbstractClass 抽象类, 类中实现了模板方法(template),定义了算法的骨架,具体子类需要去实现 其它的抽象方法 operationr2,3,4
-
ConcreteClass 实现抽象方法 operationr2,3,4, 以完成算法中特点子类的步骤
public class Client {public static void main(String[] args) {//制作红豆豆浆System.out.println("----制作红豆豆浆----");SoyaMilk redBeanSoyaMilk = new RedBeanSoyaMilk();redBeanSoyaMilk.make();System.out.println("---- 制 作 花 生 豆 浆 ----");SoyaMilk peanutSoyaMilk = new PeanutSoyaMilk();peanutSoyaMilk.make();}
}
public class PeanutSoyaMilk extends SoyaMilk {@Overridevoid addCondiments() {System.out.println(" 加入上好的花生 ");}
}
public class RedBeanSoyaMilk extends SoyaMilk {@Overridevoid addCondiments() {System.out.println(" 加入上好的红豆 ");}
}
//抽象类,表示豆浆
public abstract class SoyaMilk {//模板方法, make , 模板方法可以做成 final , 不让子类去覆盖.final void make() {select();addCondiments();soak();beat();}//选材料void select() {System.out.println("第一步:选择好的新鲜黄豆 ");}//添加不同的配料, 抽象方法, 子类具体实现abstract void addCondiments();//浸泡void soak() {System.out.println("第三步, 黄豆和配料开始浸泡, 需要 3 小时 ");}void beat() {System.out.println("第四步:黄豆和配料放到豆浆机去打碎 ");}
}//钩子方法
public abstract class SoyaMilk {//模板方法, make , 模板方法可以做成 final , 不让子类去覆盖. final void make() {select(); if(customerWantCondiments()) {
addCondiments();
}
soak();
beat();
}
//选材料
void select() {
System.out.println("第一步:选择好的新鲜黄豆 ");
}
//添加不同的配料, 抽象方法, 子类具体实现
abstract void addCondiments();//浸泡
void soak() {
System.out.println("第三步, 黄豆和配料开始浸泡, 需要 3 小时 ");
}void beat() {
System.out.println("第四步:黄豆和配料放到豆浆机去打碎 ");
}//钩子方法,决定是否需要添加配料 boolean customerWantCondiments() {
return true;}
}
模板方法模式的注意事项和细节
-
基本思想是:算法只存在于一个地方,也就是在父类中,容易修改。需要修改算法时,只要修改父类的模板方法或者已经实现的某些步骤,子类就会继承这些修改
-
实现了最大化代码复用。父类的模板方法和已实现的某些步骤会被子类继承而直接使用。
-
既统一了算法,也提供了很大的灵活性。父类的模板方法确保了算法的结构保持不变,同时由子类提供部分步骤的实现。
-
该模式的不足之处:每一个不同的实现都需要一个子类实现,导致类的个数增加,使得系统更加庞大
-
一般模板方法都加上 final 关键字, 防止子类重写模板方法.
-
模板方法模式使用场景:当要完成在某个过程,该过程要执行一系列步骤 ,这一系列的步骤基本相同,但其个别步骤在实现时 可能不同,通常考虑用模板方法模式来处理
十四、命令模式
命令模式基本介绍
- 命令模式(Command Pattern):在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,
我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计
-
命名模式使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活,实现解耦。
-
在命名模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求(即命名),同时命令模式也支持可撤销的操作。
-
通俗易懂的理解:将军发布命令,士兵去执行。其中有几个角色:将军(命令发布者)、士兵(命令的具体执行者)、命令(连接将军和士兵)。
Invoker 是调用者(将军),Receiver 是被调用者(士兵),MyCommand 是命令,实现了 Command 接口,持有接收对象
public class Client {public static void main(String[] args) {//使用命令设计模式,完成通过遥控器,对电灯的操作//创建电灯的对象(接受者)LightReceiver lightReceiver = new LightReceiver();//创建电灯相关的开关命令LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver);LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver);//需要一个遥控器RemoteController remoteController = new RemoteController();//给我们的遥控器设置命令, 比如 no = 0 是电灯的开和关的操作remoteController.setCommand(0, lightOnCommand, lightOffCommand);System.out.println("--------按下灯的开按钮-----------");remoteController.onButtonWasPushed(0);System.out.println("--------按下灯的关按钮-----------");remoteController.offButtonWasPushed(0);System.out.println("--------按下撤销按钮-----------");remoteController.undoButtonWasPushed();System.out.println("=========使用遥控器操作电视机==========");TVReceiver tvReceiver = new TVReceiver();TVOffCommand tvOffCommand = new TVOffCommand(tvReceiver);TVOnCommand tvOnCommand = new TVOnCommand(tvReceiver);//给我们的遥控器设置命令, 比如 no = 1 是电视机的开和关的操作remoteController.setCommand(1, tvOnCommand, tvOffCommand);System.out.println("--------按下电视机的开按钮-----------");remoteController.onButtonWasPushed(1);System.out.println("--------按下电视机的关按钮-----------");remoteController.offButtonWasPushed(1);System.out.println("-------- 按 下 撤 销 按 钮 -----------");remoteController.undoButtonWasPushed();}
}
//创建命令接口
public interface Command {//执行动作(操作)public void execute();//撤销动作(操作)public void undo();
}public class LightOffCommand implements Command {// 聚 合 LightReceiverLightReceiver light;// 构造器public LightOffCommand(LightReceiver light) {super();this.light = light;}@Overridepublic void execute() {// 调用接收者的方法light.off();}@Overridepublic void undo() {// 调用接收者的方法light.on();}
}public class LightOnCommand implements Command {//聚合 LightReceiverLightReceiver light;//构造器public LightOnCommand(LightReceiver light) {super();this.light = light;}@Overridepublic void execute() {//调用接收者的方法light.on();}@Overridepublic void undo() {//调用接收者的方法light.off();}
}
public class LightReceiver {public void on() {System.out.println(" 电灯打开了.. ");}public void off() {System.out.println(" 电灯关闭了.. ");}
}
/*** 没有任何命令,即空执行: 用于初始化每个按钮, 当调用空命令时,对象什么都不做* 其实,这样是一种设计模式, 可以省掉对空判断*/
public class NoCommand implements Command {@Overridepublic void execute() {}@Overridepublic void undo() {}
}public class RemoteController {// 开 按钮的命令数组Command[] onCommands;Command[] offCommands;// 执行撤销的命令Command undoCommand;// 构造器,完成对按钮初始化public RemoteController() {onCommands = new Command[5];offCommands = new Command[5];for (int i = 0; i < 5; i++) {onCommands[i] = new NoCommand();offCommands[i] = new NoCommand();}}// 给我们的按钮设置你需要的命令public void setCommand(int no, Command onCommand, Command offCommand) {onCommands[no] = onCommand;offCommands[no] = offCommand;}// 按下开按钮public void onButtonWasPushed(int no) { // no 0// 找到你按下的开的按钮, 并调用对应方法onCommands[no].execute();// 记录这次的操作,用于撤销undoCommand = onCommands[no];}// 按下开按钮public void offButtonWasPushed(int no) { // no 0// 找到你按下的关的按钮, 并调用对应方法offCommands[no].execute();// 记录这次的操作,用于撤销undoCommand = offCommands[no];}// 按下撤销按钮public void undoButtonWasPushed() {undoCommand.undo();}
}public class TVOffCommand implements Command {// 聚 合 TVReceiverTVReceiver tv;// 构造器public TVOffCommand(TVReceiver tv) {super();this.tv = tv;}@Overridepublic void execute() {// 调用接收者的方法tv.off();}@Overridepublic void undo() {// 调用接收者的方法tv.on();}
}public class TVOnCommand implements Command {// 聚 合 TVReceiverTVReceiver tv;// 构造器public TVOnCommand(TVReceiver tv) {super();this.tv = tv;}@Overridepublic void execute() {// 调用接收者的方法tv.on();}@Overridepublic void undo() {// 调用接收者的方法tv.off();}
}
public class TVReceiver {public void on() {System.out.println(" 电视机打开了.. ");}public void off() {System.out.println(" 电视机关闭了.. ");}
}
命令模式的注意事项和细节
-
将发起请求的对象与执行请求的对象解耦。发起请求的对象是调用者,调用者只要调用命令对象的 execute()方法就可以让接收者工作,而不必知道具体的接收者对象是谁、是如何实现的,命令对象会负责让接收者执行请求的动作,也就是说:”请求发起者”和“请求执行者”之间的解耦是通过命令对象实现的,命令对象起到了纽带桥梁的作用。
-
容易设计一个命令队列。只要把命令对象放到列队,就可以多线程的执行命令
-
容易实现对请求的撤销和重做
-
命令模式不足:可能导致某些系统有过多的具体命令类,增加了系统的复杂度,这点在在使用的时候要注意
-
空命令也是一种设计模式,它为我们省去了判空的操作。在上面的实例中,如果没有用空命令,我们每按下一个按键都要判空,这给我们编码带来一定的麻烦。
-
命令模式经典的应用场景:界面的一个按钮都是一条命令、模拟 CMD(DOS 命令)订单的撤销/恢复、触发- 反馈机制
十五、访问者模式
访问者模式基本介绍
-
访问者模式(Visitor Pattern),封装一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
-
主要将数据结构与数据操作分离,解决 数据结构和操作耦合性问题
-
访问者模式的基本工作原理是:在被访问的类里面加一个对外提供接待访问者的接口
-
访问者模式主要应用场景是:需要对一个对象结构中的对象进行很多不同操作(这些操作彼此没有关联),同时需要避免让这些操作"污染"这些对象的类,可以选用访问者模式解决
访问者模式的原理类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tlU56f1a-1598954057084)(file:///C:\Users\lisensen\AppData\Local\Temp\ksohtml\wps4D16.tmp.png)] |
Ø 对原理类图的说明-
即(访问者模式的角色及职责)
-
Visitor 是抽象访问者,为该对象结构中的 ConcreteElement 的每一个类声明一个 visit 操作
-
ConcreteVisitor :是一个具体的访问值 实现每个有 Visitor 声明的操作,是每个操作实现的部分.
-
ObjectStructure 能枚举它的元素, 可以提供一个高层的接口,用来允许访问者访问元素
-
Element 定义一个 accept 方法,接收一个访问者对象
-
ConcreteElement 为具体元素,实现了 accept 方法
package com.desigin.demo.visitor;public class Fail extends Action {@Overridepublic void getManResult(Man man) {System.out.println(" 男人给的评价该歌手失败 !");}@Overridepublic void getWomanResult(Woman woman) {System.out.println(" 女人给的评价该歌手失败 !");}
}package com.desigin.demo.visitor;public class Man extends Person {@Overridepublic void accept(Action action) {action.getManResult(this);}
}
package com.desigin.demo.visitor;import java.util.LinkedList;
import java.util.List;//数据结构,管理很多人(Man , Woman)
public class ObjectStructure {//维护了一个集合private List<Person> persons = new LinkedList<>();//增加到 listpublic void attach(Person p) {persons.add(p);}//移除public void detach(Person p) {persons.remove(p);}//显示测评情况public void display(Action action) {for (Person p : persons) {p.accept(action);}}
}package com.desigin.demo.visitor;public abstract class Person {/*** 提供一个方法,让访问者可以访问* @param action a*/public abstract void accept(Action action);
}
public class Success extends Action {@Overridepublic void getManResult(Man man) {System.out.println(" 男人给的评价该歌手很成功 !");}@Overridepublic void getWomanResult(Woman woman) {System.out.println(" 女人给的评价该歌手很成功 !");}
}
public class Wait extends Action {@Overridepublic void getManResult(Man man) {System.out.println(" 男人给的评价是该歌手待定 ..");}@Overridepublic void getWomanResult(Woman woman) {System.out.println(" 女人给的评价是该歌手待定 ..");}
}
//说明
//1. 这里我们使用到了双分派, 即首先在客户端程序中,将具体状态作为参数传递 Woman 中(第一次分派)
//2. 然后 Woman 类调用作为参数的 "具体方法" 中方法 getWomanResult, 同时将自己(this)作为参数
// 传入,完成第二次的分派
public class Woman extends Person {@Overridepublic void accept(Action action) {action.getWomanResult(this);}
}
public abstract class Action {//得到男性 的测评public abstract void getManResult(Man man);//得到女的 测评public abstract void getWomanResult(Woman woman);
}
public class Client {public static void main(String[] args) {//创建 ObjectStructureObjectStructure objectStructure = new ObjectStructure();objectStructure.attach(new Man());objectStructure.attach(new Woman());//成功Success success = new Success();objectStructure.display(success);System.out.println("===============");Fail fail = new Fail();objectStructure.display(fail);System.out.println("=======给的是待定的测评========");Wait wait = new Wait();objectStructure.display(wait);}
}
应用案例的小结-双分派
-
上面提到了双分派,所谓双分派是指不管类怎么变化,我们都能找到期望的方法运行。双分派意味着得到执行的操作取决于请求的种类和两个接收者的类型
-
以上述实例为例,假设我们要添加一个 *Wait* 的状态类,考察 *Man* 类和 *Woman* 类的反应,由于使用了双分派,只需增加一个 Action 子类即可在客户端调用即可,不需要改动任何其他类的代码。
访问者模式的注意事项和细节
Ø 优点
-
访问者模式符合单一职责原则、让程序具有优秀的扩展性、灵活性非常高
-
访问者模式可以对功能进行统一,可以做报表、UI、拦截器与过滤器,适用于数据结构相对稳定的系统
Ø 缺点
-
具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的, 这样造成了具体元素变更比较困难
-
违背了依赖倒转原则。访问者依赖的是具体元素,而不是抽象元素
因此,如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式就是比较合适的
十六、迭代器模式
迭代器模式基本介绍
-
迭代器模式(Iterator Pattern)是常用的设计模式,属于行为型模式
-
如果我们的集合元素是用不同的方式实现的,有数组,还有 java 的集合类,或者还有其他方式,当客户端要遍历这些集合元素的时候就要使用多种遍历方式,而且还会暴露元素的内部结构,可以考虑使用迭代器模式解决。
迭代器模式,提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表示,即:不暴露其内部的结构
package com.desigin.demo.iterator;import java.util.ArrayList;
import java.util.List;public class Client {public static void main(String[] args) {//创建学院List<College> collegeList = new ArrayList<College>();ComputerCollege computerCollege = new ComputerCollege();InfoCollege infoCollege = new InfoCollege();collegeList.add(computerCollege);collegeList.add(infoCollege);OutPutImpl outPutImpl = new OutPutImpl(collegeList);outPutImpl.printCollege();}
}
package com.desigin.demo.iterator;import java.util.Iterator;public interface College {public String getName();//增加系的方法public void addDepartment(String name, String desc);//返回一个迭代器,遍历public Iterator createIterator();
}
package com.desigin.demo.iterator;import java.util.Iterator;public class ComputerCollege implements College {Department[] departments;int numOfDepartment = 0;// 保存当前数组的对象个数public ComputerCollege() {departments = new Department[5];addDepartment("Java 专业", " Java 专业 ");addDepartment("PHP 专业", " PHP 专业 ");addDepartment("大数据专业", " 大数据专业 ");}@Overridepublic String getName() {return "计算机学院";}@Overridepublic void addDepartment(String name, String desc) {Department department = new Department(name, desc);departments[numOfDepartment] = department;numOfDepartment += 1;}@Overridepublic Iterator createIterator() {return new ComputerCollegeIterator(departments);}
}
package com.desigin.demo.iterator;import java.util.Iterator;public class ComputerCollegeIterator implements Iterator {//这里我们需要 Department 是以怎样的方式存放=>数组Department[] departments;int position = 0; //遍历的位置public ComputerCollegeIterator(Department[] departments) {this.departments = departments;}//判断是否还有下一个元素 @Override@Overridepublic boolean hasNext() {if (position >= departments.length || departments[position] == null) {return false;} else {return true;}}@Overridepublic Object next() {Department department = departments[position];position += 1;return department;}//删除的方法,默认空实现@Overridepublic void remove() {}
}
package com.desigin.demo.iterator;//系
public class Department {private String name;private String desc;public Department(String name, String desc) {super();this.name = name;this.desc = desc;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getDesc() {return desc;}public void setDesc(String desc) {this.desc = desc;}}
package com.desigin.demo.iterator;import java.util.Iterator;
import java.util.List;public class InfoColleageIterator implements Iterator {List<Department> departmentList; // 信息工程学院是以 List 方式存放系int index = -1;//索引public InfoColleageIterator(List<Department> departmentList) {this.departmentList = departmentList;}//判断 list 中还有没有下一个元素@Overridepublic boolean hasNext() {if (index >= departmentList.size() - 1) {return false;} else {index += 1;return true;}}@Overridepublic Object next() {return departmentList.get(index);}// 空 实 现 remove@Overridepublic void remove() {}
}
package com.desigin.demo.iterator;import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;public class InfoCollege implements College {List<Department> departmentList;public InfoCollege() {departmentList = new ArrayList<Department>();addDepartment("信息安全专业", " 信息安全专业 ");addDepartment("网络安全专业", " 网络安全专业 ");addDepartment("服务器安全专业", " 服务器安全专业 ");}@Overridepublic String getName() { return "信息工程学院";}@Overridepublic void addDepartment(String name, String desc) {Department department = new Department(name, desc);departmentList.add(department);}@Overridepublic Iterator createIterator() {return new InfoColleageIterator(departmentList);}
}
package com.desigin.demo.iterator;import java.util.Iterator;
import java.util.List;public class OutPutImpl {//学院集合List<College> collegeList;public OutPutImpl(List<College> collegeList) {this.collegeList = collegeList;}//遍历所有学院,然后调用 printDepartment 输出各个学院的系public void printCollege() {//从 collegeList 取出所有学院, Java 中的 List 已经实现 IteratorIterator<College> iterator = collegeList.iterator();while (iterator.hasNext()) {//取出一个学院College college = iterator.next();System.out.println("=== " + college.getName() + "=====");printDepartment(college.createIterator()); //得到对应迭代器}}//输出 学院输出 系public void printDepartment(Iterator iterator) {while (iterator.hasNext()) {Department d = (Department) iterator.next();System.out.println(d.getName());}}
}
迭代器模式的注意事项和细节
Ø 优点
-
提供一个统一的方法遍历对象,客户不用再考虑聚合的类型,使用一种方法就可以遍历对象了。
-
隐藏了聚合的内部结构,客户端要遍历聚合的时候只能取到迭代器,而不会知道聚合的具体组成。
-
提供了一种设计思想,就是一个类应该只有一个引起变化的原因(叫做单一责任原则)。在聚合类中,我们把迭代器分开,就是要把管理对象集合和遍历对象集合的责任分开,这样一来集合改变的话,只影响到聚合对象。而如果遍历方式改变的话,只影响到了迭代器。
-
当要展示一组相似对象,或者遍历一组相同对象时使用, 适合使用迭代器模式
Ø 缺点
每个聚合对象都要一个迭代器,会生成多个迭代器不好管理类
十七、观察者模式
观察者模式基本介绍
对象之间多对一依赖的一种设计方案,被依赖的对象为 Subject,依赖的对象为 Observer,Subject
通知 Observer 变化,比如这里的奶站是 Subject,是 1 的一方。用户时 Observer,是多的一方
package com.desigin.demo.observer;public class BaiduSite implements Observer {// 温度,气压,湿度private float temperature;private float pressure;private float humidity;// 更新 天气情况,是由 WeatherData 来调用,我使用推送模式@Overridepublic void update(float temperature, float pressure, float humidity) {this.temperature = temperature;this.pressure = pressure;this.humidity = humidity;display();}// 显 示public void display() {System.out.println("===百度网站====");System.out.println("***百度网站 气温 : " + temperature + "***");System.out.println("***百度网站 气压: " + pressure + "***");System.out.println("***百度网站 湿度: " + humidity + "***");}
}
package com.desigin.demo.observer;public class Client {public static void main(String[] args) {//创建一个 WeatherDataWeatherData weatherData = new WeatherData();//创建观察者CurrentConditions currentConditions = new CurrentConditions();BaiduSite baiduSite = new BaiduSite();// 注 册 到 weatherDataweatherData.registerObserver(currentConditions);weatherData.registerObserver(baiduSite);// 测 试System.out.println("通知各个注册的观察者, 看看信息");weatherData.setData(10f, 100f, 30.3f);weatherData.removeObserver(currentConditions);//测试System.out.println("通知各个注册的观察者, 看看信息");weatherData.setData(10f, 100f, 30.3f);}
}
package com.desigin.demo.observer;public class CurrentConditions implements Observer {// 温度,气压,湿度private float temperature;private float pressure;private float humidity;// 更新 天气情况,是由 WeatherData 来调用,我使用推送模式@Overridepublic void update(float temperature, float pressure, float humidity) {this.temperature = temperature;this.pressure = pressure;this.humidity = humidity;display();}// 显 示public void display() {System.out.println("***Today mTemperature: " + temperature + "***");System.out.println("***Today mPressure: " + pressure + "***");System.out.println("***Today mHumidity: " + humidity + "***");}
}
package com.desigin.demo.observer;//观察者接口,有观察者来实现
public interface Observer {public void update(float temperature, float pressure, float humidity);
}
package com.desigin.demo.observer;//被观察者接口, 让 WeatherData 来实现
public interface Subject {public void registerObserver(Observer o);public void removeObserver(Observer o);public void notifyObservers();
}
package com.desigin.demo.observer;import java.util.ArrayList;/*** 类是核心* 1. 包含最新的天气情况信息* 2. 含有 观察者集合,使用 ArrayList 管理* 3. 当数据有更新时,就主动的调用 ArrayList, 通知所有的(接入方)就看到最新的信息** @author Administrator*/
public class WeatherData implements Subject {private float temperature;private float pressure;private float humidity;//观察者集合private ArrayList<Observer> observers;//加入新的第三方public WeatherData() {observers = new ArrayList<>();}public float getTemperature() {return temperature;}public float getPressure() {return pressure;}public float getHumidity() {return humidity;}public void dataChange() {//调用 接入方的 updatenotifyObservers();}//当数据有更新时,就调用 setDatapublic void setData(float temperature, float pressure, float humidity) {this.temperature = temperature;this.pressure = pressure;this.humidity = humidity;//调用 dataChange, 将最新的信息 推送给 接入方 currentConditionsdataChange();}//注册一个观察者@Overridepublic void registerObserver(Observer o) {observers.add(o);}//移除一个观察者@Overridepublic void removeObserver(Observer o) {observers.remove(o);}//遍历所有的观察者,并通知@Overridepublic void notifyObservers() {for (Observer observer : observers) {observer.update(this.temperature, this.pressure, this.humidity);}}
}
观察者模式的好处
-
观察者模式设计后,会以集合的方式来管理用户(Observer),包括注册,移除和通知。
-
这样,我们增加观察者(这里可以理解成一个新的公告板),就不需要去修改核心类 WeatherData 不会修改代码, 遵守了 ocp 原则。
十八、中介者模式
中介者模式基本介绍
-
中介者模式(Mediator Pattern),用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互
-
中介者模式属于行为型模式,使代码易于维护
-
比如 MVC 模式,C(Controller 控制器)是 M(Model 模型)和 V(View 视图)的中介者,在前后端交互时起到了中间人的作用
package com.desigin.demo.mediator;//具体的同事类
public class Alarm extends Colleague {//构造器public Alarm(Mediator mediator, String name) {super(mediator, name);//在创建 Alarm 同事对象时,将自己放入到 ConcreteMediator 对象中[集合]mediator.register(name, this);}public void sendAlarm(int stateChange) {sendMessage(stateChange);}@Overridepublic void sendMessage(int stateChange) {// 调 用 的 中 介 者 对 象 的 getMessagethis.getMediator().getMessage(stateChange, this.name);}
}
package com.desigin.demo.mediator;public class ClientTest {public static void main(String[] args) {//创建一个中介者对象Mediator mediator = new ConcreteMediator();//创建 Alarm 并且加入到 ConcreteMediator 对象的 HashMapAlarm alarm = new Alarm(mediator, "alarm");//创建了 CoffeeMachine 对象,并 且加入到 ConcreteMediator 对象的 HashMapCoffeeMachine coffeeMachine = new CoffeeMachine(mediator, "coffeeMachine");//创建 Curtains , 并且加入到 ConcreteMediator 对象的 HashMapCurtains curtains = new Curtains(mediator, "curtains");TV tV = new TV(mediator, "TV");//让闹钟发出消息alarm.sendAlarm(0);coffeeMachine.finishCoffee();alarm.sendAlarm(1);}
}
package com.desigin.demo.mediator;public class CoffeeMachine extends Colleague {public CoffeeMachine(Mediator mediator, String name) {super(mediator, name);mediator.register(name, this);}@Overridepublic void sendMessage(int stateChange) {this.getMediator().getMessage(stateChange, this.name);}public void startCoffee() {System.out.println("It's time to startcoffee!");}public void finishCoffee() {System.out.println("After 5 minutes!");System.out.println("Coffee is ok!");sendMessage(0);}
}
package com.desigin.demo.mediator;//同事抽象类
public abstract class Colleague {private Mediator mediator;public String name;public Colleague(Mediator mediator, String name) {this.mediator = mediator;this.name = name;}public Mediator getMediator() {return this.mediator;}public abstract void sendMessage(int stateChange);
}
package com.desigin.demo.mediator;import java.util.HashMap;//具体的中介者类
public class ConcreteMediator extends Mediator {//集合,放入所有的同事对象private HashMap<String, Colleague> colleagueMap;private HashMap<String, String> interMap;public ConcreteMediator() {colleagueMap = new HashMap<String, Colleague>();interMap = new HashMap<String, String>();}@Overridepublic void register(String colleagueName, Colleague colleague) {colleagueMap.put(colleagueName, colleague);if (colleague instanceof Alarm) {interMap.put("Alarm", colleagueName);} else if (colleague instanceof CoffeeMachine) {interMap.put("CoffeeMachine", colleagueName);} else if (colleague instanceof TV) {interMap.put("TV", colleagueName);} else if (colleague instanceof Curtains) {interMap.put("Curtains", colleagueName);}}//具体中介者的核心方法//1. 根据得到消息,完成对应任务//2. 中介者在这个方法,协调各个具体的同事对象,完成任务@Overridepublic void getMessage(int stateChange, String colleagueName) {//处理闹钟发出的消息if (colleagueMap.get(colleagueName) instanceof Alarm) {if (stateChange == 0) {((CoffeeMachine) (colleagueMap.get(interMap.get("CoffeeMachine")))).startCoffee();((TV) (colleagueMap.get(interMap.get("TV")))).startTv();} else if (stateChange == 1) {((TV) (colleagueMap.get(interMap.get("TV")))).stopTv();}} else if (colleagueMap.get(colleagueName) instanceof CoffeeMachine) {((Curtains) (colleagueMap.get(interMap.get("Curtains")))).upCurtains();} else if (colleagueMap.get(colleagueName) instanceof TV) {//如果 TV 发现消息} else if (colleagueMap.get(colleagueName) instanceof Curtains) {//如果是以窗帘发出的消息,这里处理...}}@Overridepublic void sendMessage() {}
}
package com.desigin.demo.mediator;public class Curtains extends Colleague {public Curtains(Mediator mediator, String name) {super(mediator, name);mediator.register(name, this);}@Overridepublic void sendMessage(int stateChange) {this.getMediator().getMessage(stateChange, this.name);}public void upCurtains() {System.out.println("I am holding Up Curtains!");}
}
package com.desigin.demo.mediator;public abstract class Mediator {//将给中介者对象,加入到集合中public abstract void register(String colleagueName, Colleague colleague);//接收消息, 具体的同事对象发出public abstract void getMessage(int stateChange, String colleagueName);public abstract void sendMessage();
}
package com.desigin.demo.mediator;public class TV extends Colleague {public TV(Mediator mediator, String name) {super(mediator, name);mediator.register(name, this);}@Overridepublic void sendMessage(int stateChange) { this.getMediator().getMessage(stateChange, this.name);}public void startTv() { System.out.println("It's time to StartTv!");}public void stopTv() { System.out.println("StopTv!");}
}
中介者模式的注意事项和细节
-
多个类相互耦合,会形成网状结构, 使用中介者模式将网状结构分离为星型结构,进行解耦
-
减少类间依赖,降低了耦合,符合迪米特原则
-
中介者承担了较多的责任,一旦中介者出现了问题,整个系统就会受到影响
如果设计不当,中介者对象本身变得过于复杂,这点在实际使用时,要特别注意
十九、备忘录模式
备忘录模式基本介绍
-
备忘录模式(Memento Pattern)在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态
-
可以这里理解备忘录模式:现实生活中的备忘录是用来记录某些要去做的事情,或者是记录已经达成的共同意见的事情,以防忘记了。而在软件层面,备忘录模式有着相同的含义,备忘录对象主要用来记录一个对象的某种状态,或者某些数据,当要做回退时,可以从备忘录对象里获取原来的数据进行恢复操作
-
备忘录模式属于行为型模式
package com.desigin.demo.memento;import java.util.ArrayList;
import java.util.List;public class Caretaker {//在 List 集合中会有很多的备忘录对象private List<Memento> mementoList = new ArrayList<>();public void add(Memento memento) {mementoList.add(memento);}//获取到第 index 个 Originator 的 备忘录对象(即保存状态)public Memento get(int index) {return mementoList.get(index);}
}
package com.desigin.demo.memento;public class Client {public static void main(String[] args) {Originator originator = new Originator();Caretaker caretaker = new Caretaker();originator.setState(" 状态#1 攻击力 100 ");//保存了当前的状态caretaker.add(originator.saveStateMemento());originator.setState(" 状 态 #2 攻 击 力 80 ");caretaker.add(originator.saveStateMemento());originator.setState(" 状 态 #3 攻 击 力 50 ");caretaker.add(originator.saveStateMemento());System.out.println("当前的状态是 =" + originator.getState());//希望得到状态 1, 将 originator 恢复到状态 1originator.getStateFromMemento(caretaker.get(0));System.out.println("恢复到状态 1 , 当前的状态是");System.out.println("当前的状态是 =" + originator.getState());}
}
package com.desigin.demo.memento;public class Memento {private String state;//构造器public Memento(String state) {super();this.state = state;}public String getState() {return state;}
}
package com.desigin.demo.memento;public class Originator {private String state;//状态信息public String getState() {return state;}public void setState(String state) {this.state = state;}//编写一个方法,可以保存一个状态对象 Memento//因此编写一个方法,返回 Mementopublic Memento saveStateMemento() {return new Memento(state);}//通过备忘录对象,恢复状态public void getStateFromMemento(Memento memento) {state = memento.getState();}
}
游戏角色恢复状态实例
package com.desigin.demo.memento.game;//守护者对象, 保存游戏角色的状态
public class Caretaker {//如果只保存一次状态private Memento memento;
//对 GameRole 保存多次状态
//private ArrayList<Memento> mementos;
//对多个游戏角色保存多个状态
//private HashMap<String, ArrayList<Memento>> rolesMementos;public Memento getMemento() {return memento;}public void setMemento(Memento memento) {this.memento = memento;}
}
package com.desigin.demo.memento.game;public class Client {public static void main(String[] args) {//创建游戏角色GameRole gameRole = new GameRole();gameRole.setVit(100);gameRole.setDef(100);System.out.println("和 boss 大战前的状态");gameRole.display();//把当前状态保存 caretakerCaretaker caretaker = new Caretaker();caretaker.setMemento(gameRole.createMemento());System.out.println("和 boss 大战~~~");gameRole.setDef(30);gameRole.setVit(30);gameRole.display();System.out.println("大战后,使用备忘录对象恢复到战前");gameRole.recoverGameRoleFromMemento(caretaker.getMemento());System.out.println("恢复后的状态");gameRole.display();}
}
package com.desigin.demo.memento.game;public class GameRole {private int vit;private int def;//创建 Memento ,即根据当前的状态得到 Mementopublic Memento createMemento() {return new Memento(vit, def);}//从备忘录对象,恢复 GameRole 的状态public void recoverGameRoleFromMemento(Memento memento) {this.vit = memento.getVit();this.def = memento.getDef();}//显示当前游戏角色的状态public void display() {System.out.println("游戏角色当前的攻击力:" + this.vit + " 防御力: " + this.def);}public int getVit() {return vit;}public void setVit(int vit) {this.vit = vit;}public int getDef() {return def;}public void setDef(int def) {this.def = def;}
}
package com.desigin.demo.memento.game;public class Memento {//攻击力private int vit;//防御力private int def;public Memento(int vit, int def) {super();this.vit = vit;this.def = def;}public int getVit() {return vit;}public void setVit(int vit) {this.vit = vit;}public int getDef() {return def;}public void setDef(int def) {this.def = def;}
}
备忘录模式的注意事项和细节
-
给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态
-
实现了信息的封装,使得用户不需要关心状态的保存细节
-
如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存, 这个需要注意
-
适用的应用场景:
1、后悔药。
2、打游戏时的存档。
3、Windows 里的 ctri + z。
4、IE 中的后退。
5、数据库的事务管理
- 为了节约内存,备忘录模式可以和原型模式配合使用
二十、解释器模式
解释器模式基本介绍
-
在编译原理中,一个算术表达式通过词法分析器形成词法单元,而后这些词法单元再通过语法分析器构建语法分析树,最终形成一颗抽象的语法分析树。这里的词法分析器和语法分析器都可以看做是解释器
-
解释器模式(Interpreter Pattern):是指给定一个语言(表达式),定义它的文法的一种表示,并定义一个解释器, 使用该解释器来解释语言中的句子****(****表达式)
解释器模式来实现四则
package com.desigin.demo.interpreter;import java.util.HashMap;/*** 加法解释器** @author Administrator*/
public class AddExpression extends SymbolExpression {public AddExpression(Expression left, Expression right) {super(left, right);}//处理相加//var 仍然是 {a=10,b=20}..//一会我们 debug 源码,就 ok@Overridepublic int interpreter(HashMap<String, Integer> var) {//super.left.interpreter(var) : 返回 left 表达式对应的值 a = 10//super.right.interpreter(var): 返回 right 表达式对应值 b = 20return super.left.interpreter(var) + super.right.interpreter(var);}
}
package com.desigin.demo.interpreter;import java.util.HashMap;
import java.util.Stack;public class Calculator {// 定义表达式private Expression expression;// 构造函数传参,并解析public Calculator(String expStr) { // expStr = a+b// 安排运算先后顺序Stack<Expression> stack = new Stack<>();// 表达式拆分成字符数组char[] charArray = expStr.toCharArray();// [a, +, b]Expression left = null;Expression right = null;//遍历我们的字符数组, 即遍历 [a, +, b]//针对不同的情况,做处理for (int i = 0; i < charArray.length; i++) {switch (charArray[i]) {case '+': //left = stack.pop();// 从 stack 取 出 left => "a"right = new VarExpression(String.valueOf(charArray[++i]));// 取出右表达式 "b"stack.push(new AddExpression(left, right));// 然后根据得到 left 和 right 构建 AddExpresson 加入stackbreak;case '-': //left = stack.pop();right = new VarExpression(String.valueOf(charArray[++i]));stack.push(new SubExpression(left, right));break;default://如果是一个 Var 就创建要给 VarExpression 对象,并 push 到 stackstack.push(new VarExpression(String.valueOf(charArray[i])));break;}}//当遍历完整个 charArray 数组后,stack 就得到最后 Expression this.expression = stack.pop();}public int run(HashMap<String, Integer> var) {//最后将表达式 a+b 和 var = {a=10,b=20}//然后传递给 expression 的 interpreter 进行解释执行return this.expression.interpreter(var);}
}
package com.desigin.demo.interpreter;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;public class ClientTest {public static void main(String[] args) throws IOException {String expStr = getExpStr(); // a+bHashMap<String, Integer> var = getValue(expStr);// var {a=10, b=20}Calculator calculator = new Calculator(expStr);System.out.println("运算结果:" + expStr + "=" + calculator.run(var));}// 获得表达式public static String getExpStr() throws IOException {System.out.print("请输入表达式:");return (new BufferedReader(new InputStreamReader(System.in))).readLine();}// 获得值映射public static HashMap<String, Integer> getValue(String expStr) throws IOException {HashMap<String, Integer> map = new HashMap<>();for (char ch : expStr.toCharArray()) {if (ch != '+' && ch != '-') {if (!map.containsKey(String.valueOf(ch))) {System.out.print("请输入" + String.valueOf(ch) + "的值:");String in = (new BufferedReader(new InputStreamReader(System.in))).readLine();map.put(String.valueOf(ch), Integer.valueOf(in));}}}return map;}}
package com.desigin.demo.interpreter;import java.util.HashMap;/*** 抽象类表达式,通过 HashMap 键值对, 可以获取到变量的值** @author Administrator*/
public abstract class Expression {// a + b - c// 解释公式和数值, key 就是公式(表达式) 参数[a,b,c], value 就是就是具体值// HashMap {a=10, b=20}public abstract int interpreter(HashMap<String, Integer> var);
}
package com.desigin.demo.interpreter;import java.util.HashMap;public class SubExpression extends SymbolExpression {public SubExpression(Expression left, Expression right) {super(left, right);}//求出 left 和 right 表达式相减后的结果@Overridepublic int interpreter(HashMap<String, Integer> var) {return super.left.interpreter(var) - super.right.interpreter(var);}
}
package com.desigin.demo.interpreter;import java.util.HashMap;/*** 抽象运算符号解析器 这里,每个运算符号,都只和自己左右两个数字有关系,* 但左右两个数字有可能也是一个解析的结果,无论何种类型,都是 Expression 类的实现类** @author Administrator*/
public class SymbolExpression extends Expression {protected Expression left;protected Expression right;public SymbolExpression(Expression left, Expression right) {this.left = left;this.right = right;}//因为 SymbolExpression 是让其子类来实现,因此 interpreter 是一个默认实现@Overridepublic int interpreter(HashMap<String, Integer> var) {return 0;}
}
package com.desigin.demo.interpreter;import java.util.HashMap;/*** 变量的解释器** @author Administrator*/
public class VarExpression extends Expression {private String key; // key=a,key=b,key=cpublic VarExpression(String key) {this.key = key;}// var 就是{a=10, b=20}// interpreter 根据 变量名称,返回对应值@Overridepublic int interpreter(HashMap<String, Integer> var) {return var.get(this.key);}
}
解释器模式的注意事项和细节
-
当有一个语言需要解释执行,可将该语言中的句子表示为一个抽象语法树,就可以考虑使用解释器模式,让程序具有良好的扩展性
-
应用场景:编译器、运算表达式计算、正则表达式、机器人等
-
使用解释器可能带来的问题:解释器模式会引起类膨胀、解释器模式采用递归调用方法,将会导致调试非常复杂、效率可能降低.
二十一、状态模式
状态模式基本介绍
-
状态模式(State Pattern):它主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的,状态之间可以相互转换
-
当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类
package com.desigin.demo.state;import java.util.Random;/*** 可以抽奖的状态** @author Administrator*/
public class CanRaffleState extends State {RaffleActivity activity;public CanRaffleState(RaffleActivity activity) {this.activity = activity;}//已经扣除了积分,不能再扣@Overridepublic void deductMoney() {System.out.println("已经扣取过了积分");}//可以抽奖, 抽完奖后,根据实际情况,改成新的状态@Overridepublic boolean raffle() {System.out.println("正在抽奖,请稍等!");Random r = new Random();int num = r.nextInt(10);// 10%中奖机会if (num == 0) {// 改 变 活 动 状 态 为 发 放 奖 品 contextactivity.setState(activity.getDispenseState());return true;} else {System.out.println("很遗憾没有抽中奖品!");// 改变状态为不能抽奖activity.setState(activity.getNoRafflleState());return false;}}// 不能发放奖品@Overridepublic void dispensePrize() {System.out.println("没中奖,不能发放奖品");}
}
package com.desigin.demo.state;/*** 状态模式测试类** @author Administrator*/
public class ClientTest {public static void main(String[] args) {// 创建活动对象,奖品有 1 个奖品RaffleActivity activity = new RaffleActivity(1);// 我们连续抽 300 次奖for (int i = 0; i < 30; i++) {System.out.println("--------第" + (i + 1) + "次抽奖----------");// 参加抽奖,第一步点击扣除积分activity.debuctMoney();// 第二步抽奖activity.raffle();}}}
package com.desigin.demo.state;/*** 奖品发放完毕状态* 说明,当我们 activity 改变成 DispenseOutState, 抽奖活动结束** @author Administrator*/
public class DispenseOutState extends State {// 初始化时传入活动引用RaffleActivity activity;public DispenseOutState(RaffleActivity activity) {this.activity = activity;}@Overridepublic void deductMoney() {System.out.println("奖品发送完了,请下次再参加");}@Overridepublic boolean raffle() {System.out.println("奖品发送完了,请下次再参加");return false;}@Overridepublic void dispensePrize() {System.out.println("奖品发送完了,请下次再参加");}
}
package com.desigin.demo.state;/*** 发放奖品的状态** @author Administrator*/
public class DispenseState extends State {// 初始化时传入活动引用,发放奖品后改变其状态RaffleActivity activity;public DispenseState(RaffleActivity activity) {this.activity = activity;}@Overridepublic void deductMoney() {System.out.println("不能扣除积分");}@Overridepublic boolean raffle() {System.out.println("不能抽奖");return false;}//发放奖品 @Override@Overridepublic void dispensePrize() {if (activity.getCount() > 0) {System.out.println("恭喜中奖了");// 改变状态为不能抽奖activity.setState(activity.getNoRafflleState());} else {System.out.println("很遗憾,奖品发送完了");// 改变状态为奖品发送完毕, 后面我们就不可以抽奖activity.setState(activity.getDispensOutState());//System.out.println("抽奖活动结束");//System.exit(0);}}
}
package com.desigin.demo.state;/*** 不能抽奖状态** @author Administrator*/
public class NoRaffleState extends State {// 初始化时传入活动引用,扣除积分后改变其状态RaffleActivity activity;public NoRaffleState(RaffleActivity activity) {this.activity = activity;}// 当前状态可以扣积分 , 扣除后,将状态设置成可以抽奖状态@Overridepublic void deductMoney() {System.out.println("扣除 50 积分成功,您可以抽奖了");activity.setState(activity.getCanRaffleState());}// 当前状态不能抽奖@Overridepublic boolean raffle() {System.out.println("扣了积分才能抽奖喔!");return false;}// 当前状态不能发奖品@Overridepublic void dispensePrize() {System.out.println("不能发放奖品");}
}
package com.desigin.demo.state;/*** 抽奖活动 //** @author Administrator*/
public class RaffleActivity {// state 表示活动当前的状态,是变化State state = null;// 奖品数量int count = 0;// 四个属性,表示四种状态State noRafflleState = new NoRaffleState(this);State canRaffleState = new CanRaffleState(this);State dispenseState = new DispenseState(this);State dispensOutState = new DispenseOutState(this);//构造器//1. 初始化当前的状态为 noRafflleState(即不能抽奖的状态)//2. 初始化奖品的数量public RaffleActivity(int count) {this.state = getNoRafflleState();this.count = count;}//扣分, 调用当前状态的 deductMoneypublic void debuctMoney() {state.deductMoney();}//抽奖public void raffle() {// 如果当前的状态是抽奖成功if (state.raffle()) {//领取奖品state.dispensePrize();}}public State getState() {return state;}public void setState(State state) {this.state = state;}//这里请大家注意,每领取一次奖品,count--public int getCount() {int curCount = count;count--;return curCount;}public void setCount(int count) {this.count = count;}public State getNoRafflleState() {return noRafflleState;}public void setNoRafflleState(State noRafflleState) {this.noRafflleState = noRafflleState;}public State getCanRaffleState() {return canRaffleState;}public void setCanRaffleState(State canRaffleState) {this.canRaffleState = canRaffleState;}public State getDispenseState() {return dispenseState;}public void setDispenseState(State dispenseState) {this.dispenseState = dispenseState;}public State getDispensOutState() {return dispensOutState;}public void setDispensOutState(State dispensOutState) {this.dispensOutState = dispensOutState;}
}
package com.desigin.demo.state;/*** 状态抽象类** @author Administrator*/
public abstract class State {// 扣除积分 - 50public abstract void deductMoney();// 是否抽中奖品public abstract boolean raffle();// 发放奖品public abstract void dispensePrize();}
状态模式的注意事项和细节
-
代码有很强的可读性。状态模式将每个状态的行为封装到对应的一个类中
-
方便维护。将容易产生问题的 if-else 语句删除了,如果把每个状态的行为都放到一个类中,每次调用方法时都要判断当前是什么状态,不但会产出很多 if-else 语句,而且容易出错
-
符合“开闭原则”。容易增删状态
-
会产生很多类。每个状态都要一个对应的类,当状态过多时会产生很多类,加大维护难度
-
应用场景:当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同的行为的时候, 可以考虑使用状态模式
二十二、策略模式
策略模式基本介绍
-
- 策略模式(Strategy Pattern)中,定义算法族(策略组),分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户
-
2)这算法体现了几个设计原则,第一、把变化的代码从不变的代码中分离出来;第二、针对接口编程而不是具体类(定义了策略接口);第三、多用组合/聚合,少用继承(客户通过组合方式使用策略)
package com.desigin.demo.strategy;public class BadFlyBehavior implements FlyBehavior {@Overridepublic void fly() {System.out.println(" 飞翔技术一般 ");}
}
package com.desigin.demo.strategy;public class Client {public static void main(String[] args) {WildDuck wildDuck = new WildDuck();wildDuck.fly();//ToyDuck toyDuck = new ToyDuck();toyDuck.fly();PekingDuck pekingDuck = new PekingDuck();pekingDuck.fly();//动态改变某个对象的行为, 北京鸭 不能飞pekingDuck.setFlyBehavior(new NoFlyBehavior());System.out.println("北京鸭的实际飞翔能力");pekingDuck.fly();}
}
package com.desigin.demo.strategy;public abstract class Duck {//属性, 策略接口FlyBehavior flyBehavior;//其它属性<->策略接口QuackBehavior quackBehavior;public Duck() {}public abstract void display();//显示鸭子信息public void quack() {System.out.println("鸭子嘎嘎叫~~");}public void swim() {System.out.println("鸭子会游泳~~");}public void fly() {
//改进if (flyBehavior != null) {flyBehavior.fly();}}public void setFlyBehavior(FlyBehavior flyBehavior) {this.flyBehavior = flyBehavior;}public void setQuackBehavior(QuackBehavior quackBehavior) {this.quackBehavior = quackBehavior;}
}
public interface FlyBehavior {void fly(); // 子类具体实现
}
public class GoodFlyBehavior implements FlyBehavior {@Overridepublic void fly() {System.out.println(" 飞翔技术高超 ~~~");}
}
public class NoFlyBehavior implements FlyBehavior {@Overridepublic void fly() {System.out.println(" 不会飞翔 ");}
}
public class PekingDuck extends Duck {//假如北京鸭可以飞翔,但是飞翔技术一般public PekingDuck() {flyBehavior = new BadFlyBehavior();}@Overridepublic void display() {System.out.println("~~北京鸭~~~");}
}
public interface QuackBehavior {void quack();//子类实现
}
public class WildDuck extends Duck {//构造器,传入 FlyBehavor 的对象public WildDuck() {flyBehavior = new GoodFlyBehavior();}@Overridepublic void display() {System.out.println(" 这是野鸭 ");}
}
策略模式的注意事项和细节
-
策略模式的关键是:分析项目中变化部分与不变部分
-
策略模式的核心思想是:多用组合/聚合 少用继承;用行为类组合,而不是行为的继承。更有弹性
-
体现了“对修改关闭,对扩展开放”原则,客户端增加行为不用修改原有代码,只要添加一种策略(或者行为) 即可,避免了使用多重转移语句(if…else if…else)
-
提供了可以替换继承关系的办法: 策略模式将算法封装在独立的 Strategy 类中使得你可以独立于其 Context 改变它,使它易于切换、易于理解、易于扩展
需要注意的是:每添加一个策略就要增加一个类,当策略过多是会导致类数目庞
二十三、责任链模式
职责链模式基本介绍
-
职责链模式(Chain of Responsibility Pattern), 又叫 责任链模式,为请求创建了一个接收者对象的链(简单示意图)。这种模式对请求的发送者和接收者进行解耦。
-
职责链模式通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
-
这种类型的设计模式属于行为型模式
实例要求
编写程序完成学校 OA 系统的采购审批项目:需求采购员采购教学器材
如果金额 小于等于 5000, 由教学主任审批如果金额 小于等于 10000, 由院长审批
如果金额 小于等于 30000, 由副校长审批如果金额 超过 30000 以上,有校长审批
package com.desigin.demo.responsibilitychain;public abstract class Approver {Approver approver; //下一个处理者String name; // 名 字public Approver(String name) {this.name = name;}//下一个处理者public void setApprover(Approver approver) {this.approver = approver;}//处理审批请求的方法,得到一个请求, 处理是子类完成,因此该方法做成抽象public abstract void processRequest(PurchaseRequest purchaseRequest);
}
package com.desigin.demo.responsibilitychain;public class Client {public static void main(String[] args) {//创建一个请求PurchaseRequest purchaseRequest = new PurchaseRequest(1, 31000, 1);//创建相关的审批人DepartmentApprover departmentApprover = new DepartmentApprover("张主任");CollegeApprover collegeApprover = new CollegeApprover("李院长");ViceSchoolMasterApprover viceSchoolMasterApprover = new ViceSchoolMasterApprover("王副校");SchoolMasterApprover schoolMasterApprover = new SchoolMasterApprover("佟校长");//需要将各个审批级别的下一个设置好 (处理人构成环形: )departmentApprover.setApprover(collegeApprover);collegeApprover.setApprover(viceSchoolMasterApprover);viceSchoolMasterApprover.setApprover(schoolMasterApprover);schoolMasterApprover.setApprover(departmentApprover);departmentApprover.processRequest(purchaseRequest);viceSchoolMasterApprover.processRequest(purchaseRequest);}
}
package com.desigin.demo.responsibilitychain;//请求类
public class PurchaseRequest {private int type = 0; //请求类型private float price = 0.0f; //请求金额private int id = 0;//构造器public PurchaseRequest(int type, float price, int id) {this.type = type;this.price = price;this.id = id;}public int getType() {return type;}public float getPrice() {return price;}public int getId() {return id;}
}
package com.desigin.demo.responsibilitychain;public class CollegeApprover extends Approver {public CollegeApprover(String name) {super(name);}@Overridepublic void processRequest(PurchaseRequest purchaseRequest) {if (purchaseRequest.getPrice() < 5000 && purchaseRequest.getPrice() <= 10000) {System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");} else {approver.processRequest(purchaseRequest);}}
}
package com.desigin.demo.responsibilitychain;public class DepartmentApprover extends Approver {public DepartmentApprover(String name) {super(name);}@Overridepublic void processRequest(PurchaseRequest purchaseRequest) {if (purchaseRequest.getPrice() <= 5000) {System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");} else {approver.processRequest(purchaseRequest);}}
}
package com.desigin.demo.responsibilitychain;public class SchoolMasterApprover extends Approver {public SchoolMasterApprover(String name) {super(name);}@Overridepublic void processRequest(PurchaseRequest purchaseRequest) {if (purchaseRequest.getPrice() > 30000) {System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");} else {approver.processRequest(purchaseRequest);}}
}
package com.desigin.demo.responsibilitychain;public class ViceSchoolMasterApprover extends Approver {public ViceSchoolMasterApprover(String name) {super(name);}@Overridepublic void processRequest(PurchaseRequest purchaseRequest) {if (purchaseRequest.getPrice() < 10000 && purchaseRequest.getPrice() <= 30000) {System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");} else {approver.processRequest(purchaseRequest);}}
}
职责链模式的注意事项和细节
-
将请求和处理分开,实现解耦,提高系统的灵活性
-
简化了对象,使对象不需要知道链的结构
-
性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,一般通过在 Handler 中设置一个最大节点数量,在 setNext()方法中判断是否已经超过阀值,超过则不允许该链建立,避免出现超长链无意识地破坏系统性能
-
调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂
-
最佳应用场景:有多个对象可以处理同一个请求时,比如:多级请求、请假/加薪等审批流程、Java Web 中 Tomcat对 Encoding 的处理、拦截器
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
相关文章
- 跨境商家爆单类目,又爆单!
万万没想到, 上半年已经出口大火的中国自行车,在这个秋冬季节,杀了个“回马枪”, 再次爆火! 阿里巴巴国际站数据显示,自行车行业连续6个月实收GMV超过100%增长, 10月订单量同比去年增长了22…...
2024/4/26 4:58:33 - 移动端 二倍图
在移动端的时候添加的图片会自动放大,图片会变得模糊 所以可以先把图片先缩小然后让移动端再把它放大 比如需要的图片是 50px* 50px 如果移动端把图片放大的是二倍,然后可以把这张图片设置成25px x 25px然后移动端会自动放大二倍就变成了50px*50px...
2024/5/2 21:25:39 - 作业的创建与监控-kettle
作业的创建与监控-kettle 实验原理:通过“Start”设定作业执行的顺序与邮件,然后通过“Transformation”调度执行相关的转换,再通过“Success”设定作业执行完毕。创建两个转换分别给两个“Transformation”调用,创建第一个转换&…...
2024/5/2 16:44:46 - IDEA插件Easy Code使用介绍,及部分使用心得(建议仔细阅读)
IDEA插件Easy Code使用介绍,及部分使用心得(建议仔细阅读) 在IDEA下载easycode 下载完成后需要重启IDEA,进入后即可正常使用。 ** 在SpringBoot项目中使用easycode逆向生成工程 创建SpringBoot项目 这里选择springboot所需要的依赖。 最后选择finis…...
2024/4/28 2:01:08 - 深入浅出谈Java并发编程学习教程
一年前由于工作需要从微软技术栈入坑Java,并陆陆续续做了一个Java后台项目,目前在搞ScalaJava混合的后台开发,一直觉得并发编程是所有后台工程师的基本功,所以也学习了小一年Java的并发工具,对整体的并发理解乃至分布式…...
2024/5/10 5:22:13 - 一个按钮实现正序和降序
python 前端 vue<div><button click"btn()">价格排序↑↓</button> </div>data(){ return{ a:1, } }, methodes:{ orderby(){axios.get(http://127.0.0.1:8000/orderby?athis.a).then(resp>{this.goods_listresp.data.dataconsole.log(r…...
2024/4/26 12:36:34 - GB36600-2018土壤环境质量建设用地土壤污染风险管控标准
土壤45项检测是建设用地土壤检测必检项目有45项,总共有85项,剩余的40项目检测,需要根据土壤场地情况选择性检测,建筑用地土壤检测主要依据标准GB36600-2018土壤环境质量-建设用地土壤污染风险管控标准. 检测范围: 建…...
2024/5/6 5:53:24 - MIL开发实践(2)——MIL实时采图
前言前言效果图正文初始化实时采图前言 网上找到的关于MIL实时采图的代码基本上都是只有很老的代码,但其实MIL是有自带的关于采图方面的QT的demo的。这里给出地址,应该比一些普通的demo要更有使用的价值。这里说一下整体的思路:关于采集&…...
2024/5/8 21:50:48 - 返回两个正序数组的中位数
给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的中位数(没有实现题目要求时间复杂度为 O(log (mn)) 的算法,先写写记录一下,再瞅瞅大佬们的)。 package Leet…...
2024/5/10 11:13:24 - 2020年G3锅炉水处理模拟考试题库及G3锅炉水处理作业考试题库
题库来源:安全生产模拟考试一点通公众号小程序 2020年G3锅炉水处理模拟考试题库及G3锅炉水处理作业考试题库,包含G3锅炉水处理模拟考试题库答案和解析及G3锅炉水处理作业考试题库练习。由安全生产模拟考试一点通公众号结合国家G3锅炉水处理考试最新大纲…...
2024/5/10 4:29:21 - 自动化测试工程师简历(吐血整理,字句斟酌)
姓名(软件测试) 联系电话:175XXXXX 电子邮件:XXXqq.com 学 历:本 科 专 业:计算机科学与技术 毕业院校:XXX 现居住地:XXX 职业技能 功能测试: 熟悉软件测试流程、测试…...
2024/5/10 11:13:39 - 学会这 10000 个段子,成为 IT 职场幽默达人。Python 爬虫小课 8-9
现代职场达人,应该做到 有情、有趣、有用、有品。好了,去掉 “有” 字你就成了。那如何成为职场幽默达人呢,咱需要一定的素材也就是段子,多看段子才能多说段子,并且还能说高级段子。 点击就能跳转的神奇目录 爬虫小课系列文章导读链接爬取前的分析工作爬虫基础知识普及时间…...
2024/5/8 11:04:58 - 怎么理解函数的防抖和节流
防抖: 就是指触发事件后在n秒内函数只能执行一次,如果在n秒内又触发了事件,则会重新计算函数执行时间。 例如:设定1000毫秒执行,当你触发事件了,他会1000毫秒后执行,但是在还剩500毫秒的时候你又触发了事件…...
2024/5/8 9:20:40 - 大数据8_10_Scala异常隐式转换
12 异常 Scala中的异常不区分所谓的编译时异常和运行时异常也无需显示抛出方法异常,所以Scala中没有throws关键字。 scala中异常的处理方式类似于模式匹配,如果异常类型没有匹配成功,也不会发生MatchError,只是将异常直接报给了J…...
2024/5/9 1:23:39 - vue props传值失败 输出undefined的解决方法-----命名规范
问题 父组件: <goods :busiIntroData"busiIntroData"></goods>子组件(goods.vue):直接使用props接收的值显示在页面上 <text>{{另需配送费${busiIntroData.physical}元}}</text>props:{busiIntroData:Object }显示…...
2024/5/8 10:17:18 - AQS源码导读
文章目录前言AQS基础架构ReentrantLock.lock()做了什么?ReentrantLock.unlock()做了什么?问题1.工作线程什么时候出队?2.AQS唤醒队列的规则是什么?3.唤醒节点为什么要从尾巴开始?4.其他问题尾巴前言 AQS全称:Abstrac…...
2024/5/9 2:22:30 - 4.Redis的基本数据类型有哪些?嘻哈的简写笔记——Redis
Redis的主要模式就是一个Key对应一个value ,其中key就是字符串,没有什么变化而其中的value共有5中数据类型; 有String ,hash ,list , set , zset 五种数据类型; String :…...
2024/5/8 5:12:38 - GitHub入门:安装Git和使用Git(详细)
Git的作用 通过Git管理GitHub托管项目代码 下载Git Git官网下载:www.git-scm.com/download/win,安装过程比较简单,安装选择命令行Bash模式。 Git的工作区域 1、工作区(Working Directory):添加、编辑、…...
2024/5/8 19:28:41 - 7-23 分段计算居民水费 (10分)
7-23 分段计算居民水费 (10分) 题目描述如下: 为鼓励居民节约用水,自来水公司采取按用水量阶梯式计价的办法,居民应交水费y(元)与月用水量x(吨)相关:当x不超过15吨时,y…...
2024/5/8 21:50:31 - Oracle 管理员账号密码忘记的快速解决方法
本示例 Oracle12c 为例。 管理员账号分为 system 与 sys,修改方法不同,我们分别予以叙述。 1 修改 sys 密码 首先进入 Oracle 安装目录,形如 F:\app\Administrator\product\12.1.0\dbhome_1\database,删除或重命名 PWDorcl.ora…...
2024/5/9 2:51:38
最新文章
- 【初阶数据结构】单链表经典OJ题
目录标题 原题展现题目解析代码展现1.创建新节点2.拷贝random指针3.将新节点尾插 原题展现 该题是力扣上的第138题,题目链接如下:随机链表的复制。 题目解析 我们发现这个链表和一般的链表存在着一点点区别,那就是每个节点多了一个random指…...
2024/5/10 12:40:05 - 梯度消失和梯度爆炸的一些处理方法
在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言,在此感激不尽。 权重和梯度的更新公式如下: w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...
2024/5/9 21:23:04 - 自我介绍的HTML 页面(入门)
一.前情提要 1.主要是代码示例,具体内容需自己填充 2.代码后是详解 二.代码实例和解析 代码 <!DOCTYPE html> <html lang"zh-CN"> <head> <meta charset"UTF-8"> <title>自我介绍页面</title>…...
2024/5/10 6:46:31 - Redis Stack十部曲之三:理解Redis Stack中的数据类型
文章目录 前言String字符串作为计数器限制 List限制列表阻塞列表自动创建和删除聚合类型键限制 Set限制 Hash限制 Sorted Set范围操作字典操作更新分数 JSON路径限制 BitMapBitfieldProbabilisticHyperLogLogBloom filterCuckoo filtert-digestTop-KCount-min sketchConfigurat…...
2024/5/9 14:21:49 - 416. 分割等和子集问题(动态规划)
题目 题解 class Solution:def canPartition(self, nums: List[int]) -> bool:# badcaseif not nums:return True# 不能被2整除if sum(nums) % 2 ! 0:return False# 状态定义:dp[i][j]表示当背包容量为j,用前i个物品是否正好可以将背包填满ÿ…...
2024/5/10 1:36:26 - 【Java】ExcelWriter自适应宽度工具类(支持中文)
工具类 import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet;/*** Excel工具类** author xiaoming* date 2023/11/17 10:40*/ public class ExcelUti…...
2024/5/9 7:40:42 - Spring cloud负载均衡@LoadBalanced LoadBalancerClient
LoadBalance vs Ribbon 由于Spring cloud2020之后移除了Ribbon,直接使用Spring Cloud LoadBalancer作为客户端负载均衡组件,我们讨论Spring负载均衡以Spring Cloud2020之后版本为主,学习Spring Cloud LoadBalance,暂不讨论Ribbon…...
2024/5/9 2:44:26 - TSINGSEE青犀AI智能分析+视频监控工业园区周界安全防范方案
一、背景需求分析 在工业产业园、化工园或生产制造园区中,周界防范意义重大,对园区的安全起到重要的作用。常规的安防方式是采用人员巡查,人力投入成本大而且效率低。周界一旦被破坏或入侵,会影响园区人员和资产安全,…...
2024/5/10 2:07:45 - VB.net WebBrowser网页元素抓取分析方法
在用WebBrowser编程实现网页操作自动化时,常要分析网页Html,例如网页在加载数据时,常会显示“系统处理中,请稍候..”,我们需要在数据加载完成后才能继续下一步操作,如何抓取这个信息的网页html元素变化&…...
2024/5/10 8:07:24 - 【Objective-C】Objective-C汇总
方法定义 参考:https://www.yiibai.com/objective_c/objective_c_functions.html Objective-C编程语言中方法定义的一般形式如下 - (return_type) method_name:( argumentType1 )argumentName1 joiningArgument2:( argumentType2 )argumentName2 ... joiningArgu…...
2024/5/9 5:40:03 - 【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】
👨💻博客主页:花无缺 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 本文由 花无缺 原创 收录于专栏 【洛谷算法题】 文章目录 【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】🌏题目描述🌏输入格…...
2024/5/10 8:16:30 - 【ES6.0】- 扩展运算符(...)
【ES6.0】- 扩展运算符... 文章目录 【ES6.0】- 扩展运算符...一、概述二、拷贝数组对象三、合并操作四、参数传递五、数组去重六、字符串转字符数组七、NodeList转数组八、解构变量九、打印日志十、总结 一、概述 **扩展运算符(...)**允许一个表达式在期望多个参数࿰…...
2024/5/10 2:07:43 - 摩根看好的前智能硬件头部品牌双11交易数据极度异常!——是模式创新还是饮鸩止渴?
文 | 螳螂观察 作者 | 李燃 双11狂欢已落下帷幕,各大品牌纷纷晒出优异的成绩单,摩根士丹利投资的智能硬件头部品牌凯迪仕也不例外。然而有爆料称,在自媒体平台发布霸榜各大榜单喜讯的凯迪仕智能锁,多个平台数据都表现出极度异常…...
2024/5/10 2:07:43 - Go语言常用命令详解(二)
文章目录 前言常用命令go bug示例参数说明 go doc示例参数说明 go env示例 go fix示例 go fmt示例 go generate示例 总结写在最后 前言 接着上一篇继续介绍Go语言的常用命令 常用命令 以下是一些常用的Go命令,这些命令可以帮助您在Go开发中进行编译、测试、运行和…...
2024/5/9 4:12:16 - 用欧拉路径判断图同构推出reverse合法性:1116T4
http://cplusoj.com/d/senior/p/SS231116D 假设我们要把 a a a 变成 b b b,我们在 a i a_i ai 和 a i 1 a_{i1} ai1 之间连边, b b b 同理,则 a a a 能变成 b b b 的充要条件是两图 A , B A,B A,B 同构。 必要性显然࿰…...
2024/5/9 7:40:35 - 【NGINX--1】基础知识
1、在 Debian/Ubuntu 上安装 NGINX 在 Debian 或 Ubuntu 机器上安装 NGINX 开源版。 更新已配置源的软件包信息,并安装一些有助于配置官方 NGINX 软件包仓库的软件包: apt-get update apt install -y curl gnupg2 ca-certificates lsb-release debian-…...
2024/5/9 19:47:07 - Hive默认分割符、存储格式与数据压缩
目录 1、Hive默认分割符2、Hive存储格式3、Hive数据压缩 1、Hive默认分割符 Hive创建表时指定的行受限(ROW FORMAT)配置标准HQL为: ... ROW FORMAT DELIMITED FIELDS TERMINATED BY \u0001 COLLECTION ITEMS TERMINATED BY , MAP KEYS TERMI…...
2024/5/10 10:17:11 - 【论文阅读】MAG:一种用于航天器遥测数据中有效异常检测的新方法
文章目录 摘要1 引言2 问题描述3 拟议框架4 所提出方法的细节A.数据预处理B.变量相关分析C.MAG模型D.异常分数 5 实验A.数据集和性能指标B.实验设置与平台C.结果和比较 6 结论 摘要 异常检测是保证航天器稳定性的关键。在航天器运行过程中,传感器和控制器产生大量周…...
2024/5/10 2:07:41 - --max-old-space-size=8192报错
vue项目运行时,如果经常运行慢,崩溃停止服务,报如下错误 FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory 因为在 Node 中,通过JavaScript使用内存时只能使用部分内存(64位系统&…...
2024/5/9 5:02:59 - 基于深度学习的恶意软件检测
恶意软件是指恶意软件犯罪者用来感染个人计算机或整个组织的网络的软件。 它利用目标系统漏洞,例如可以被劫持的合法软件(例如浏览器或 Web 应用程序插件)中的错误。 恶意软件渗透可能会造成灾难性的后果,包括数据被盗、勒索或网…...
2024/5/9 4:31:45 - JS原型对象prototype
让我简单的为大家介绍一下原型对象prototype吧! 使用原型实现方法共享 1.构造函数通过原型分配的函数是所有对象所 共享的。 2.JavaScript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象,所以我们也称为原型对象…...
2024/5/9 16:54:42 - C++中只能有一个实例的单例类
C中只能有一个实例的单例类 前面讨论的 President 类很不错,但存在一个缺陷:无法禁止通过实例化多个对象来创建多名总统: President One, Two, Three; 由于复制构造函数是私有的,其中每个对象都是不可复制的,但您的目…...
2024/5/10 1:31:37 - python django 小程序图书借阅源码
开发工具: PyCharm,mysql5.7,微信开发者工具 技术说明: python django html 小程序 功能介绍: 用户端: 登录注册(含授权登录) 首页显示搜索图书,轮播图࿰…...
2024/5/10 9:24:29 - 电子学会C/C++编程等级考试2022年03月(一级)真题解析
C/C++等级考试(1~8级)全部真题・点这里 第1题:双精度浮点数的输入输出 输入一个双精度浮点数,保留8位小数,输出这个浮点数。 时间限制:1000 内存限制:65536输入 只有一行,一个双精度浮点数。输出 一行,保留8位小数的浮点数。样例输入 3.1415926535798932样例输出 3.1…...
2024/5/10 10:40:03 - 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...
解析如下:1、长按电脑电源键直至关机,然后再按一次电源健重启电脑,按F8健进入安全模式2、安全模式下进入Windows系统桌面后,按住“winR”打开运行窗口,输入“services.msc”打开服务设置3、在服务界面,选中…...
2022/11/19 21:17:18 - 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。
%读入6幅图像(每一幅图像的大小是564*564) f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...
2022/11/19 21:17:16 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...
win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面,在等待界面中我们需要等待操作结束才能关机,虽然这比较麻烦,但是对系统进行配置和升级…...
2022/11/19 21:17:15 - 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...
有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows,请勿关闭计算机”的提示,要过很久才能进入系统,有的用户甚至几个小时也无法进入,下面就教大家这个问题的解决方法。第一种方法:我们首先在左下角的“开始…...
2022/11/19 21:17:14 - win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...
置信有很多用户都跟小编一样遇到过这样的问题,电脑时发现开机屏幕显现“正在配置Windows Update,请勿关机”(如下图所示),而且还需求等大约5分钟才干进入系统。这是怎样回事呢?一切都是正常操作的,为什么开时机呈现“正…...
2022/11/19 21:17:13 - 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...
Win7系统开机启动时总是出现“配置Windows请勿关机”的提示,没过几秒后电脑自动重启,每次开机都这样无法进入系统,此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一:开机按下F8,在出现的Windows高级启动选…...
2022/11/19 21:17:12 - 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...
有不少windows10系统用户反映说碰到这样一个情况,就是电脑提示正在准备windows请勿关闭计算机,碰到这样的问题该怎么解决呢,现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法:1、2、依次…...
2022/11/19 21:17:11 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...
今天和大家分享一下win7系统重装了Win7旗舰版系统后,每次关机的时候桌面上都会显示一个“配置Windows Update的界面,提示请勿关闭计算机”,每次停留好几分钟才能正常关机,导致什么情况引起的呢?出现配置Windows Update…...
2022/11/19 21:17:10 - 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...
只能是等着,别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚,只能是考虑备份数据后重装系统了。解决来方案一:管理员运行cmd:net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...
2022/11/19 21:17:09 - 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?
原标题:电脑提示“配置Windows Update请勿关闭计算机”怎么办?win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢?一般的方…...
2022/11/19 21:17:08 - 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...
关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!关机提示 windows7 正在配…...
2022/11/19 21:17:05 - 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...
钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...
2022/11/19 21:17:05 - 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...
前几天班里有位学生电脑(windows 7系统)出问题了,具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面,长时间没反应,无法进入系统。这个问题原来帮其他同学也解决过,网上搜了不少资料&#x…...
2022/11/19 21:17:04 - 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...
本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法,并在最后教给你1种保护系统安全的好方法,一起来看看!电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中,添加了1个新功能在“磁…...
2022/11/19 21:17:03 - 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...
许多用户在长期不使用电脑的时候,开启电脑发现电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机。。.这要怎么办呢?下面小编就带着大家一起看看吧!如果能够正常进入系统,建议您暂时移…...
2022/11/19 21:17:02 - 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...
配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!配置windows update失败 还原更改 请勿关闭计算机&#x…...
2022/11/19 21:17:01 - 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...
不知道大家有没有遇到过这样的一个问题,就是我们的win7系统在关机的时候,总是喜欢显示“准备配置windows,请勿关机”这样的一个页面,没有什么大碍,但是如果一直等着的话就要两个小时甚至更久都关不了机,非常…...
2022/11/19 21:17:00 - 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...
当电脑出现正在准备配置windows请勿关闭计算机时,一般是您正对windows进行升级,但是这个要是长时间没有反应,我们不能再傻等下去了。可能是电脑出了别的问题了,来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...
2022/11/19 21:16:59 - 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...
我们使用电脑的过程中有时会遇到这种情况,当我们打开电脑之后,发现一直停留在一个界面:“配置Windows Update失败,还原更改请勿关闭计算机”,等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢࿰…...
2022/11/19 21:16:58 - 如何在iPhone上关闭“请勿打扰”
Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...
2022/11/19 21:16:57