PS :以作者的亲身来看,这东西对于初学者来说有用但不多,这些东西,更像一种经验的总结,在平时开发当中一般是用不到的,因此站在这个角度上用处不大。
1.工厂模式
1.1 简单工厂模式
我们把new 对象逻辑封装到一共工厂里,通过工厂去实例化对象。用户无需知道逻辑,只需要传入对应的参数就可得到对应的对象。
坏处: 假如说我们这个Me类有个新的子类,那么我们需要对Me工厂的getMeInstance逻辑修改,后面每次增加新的子类也是如此,需要增加多个if else判断。一旦子类变多,该方法就会变得臃肿,可读性变差。
1.2 工厂方法模式
我们再看工厂方法模式,就是在工厂定义一个抽象方法,用来实例化类的。 然后每次新增加一个子类,都会创建一个新的对应工厂,去实现这个方法,return一个新子类。
缺点: 每次增加一新的子类,都会增加新的具体工厂,新的具体工厂只能创建一个具体对象。
1.3 抽象工厂模式
不同于工厂方法模式,父抽象工厂只是提供一个接口,而新的子类工厂可以去实例一种产品。
关于工厂模式,我们可以采用ava反射来实例任何一个对象。
2.单例模式
// 单例模式涉及到懒加载解决线程安全的问题。
通过线程锁、 volatile:直接操作变量主内存解决安全问题。
public class SingletonDemo {
private static ReentrantLock lock = new ReentrantLock(true);
private static volatile SingletonDemo singletonDemo; // 考虑到副本 -> 主内存引用
// 双重锁检查就是考虑,内存
public static SingletonDemo getInstance(){
Condition condition = lock.newCondition();
if(singletonDemo == null){
lock.lock(); // 锁住,防止多次实例化
try {
lock.lock();
singletonDemo = new SingletonDemo();
System.out.println("我创建了");
}finally {
lock.unlock();
}
}
return singletonDemo;
}
3.生成器模式
理解:把一个复杂对象对创造,拆分成好几个对象。
就比如说,我们常见的Http对象,我们单一个创建http对象的话由很多字段,所有我们可以给他改成HttpRequest、HttpBody、Reeuqest里面拆分成Method,Contype类型等方式。
所以,我们可以把他一个对象的创造,拆分成好几个对象来构造。,当然一般默认会写默认字段值的,这种就叫生成器模式。
一般生成器模式,喜欢加一个Builder内部类,直接用来创建外部对象。
关于内部类有什么好处? 内部类,能直接访问外部的成员。
优化代码结构,相关关联的一般写成内部类。
回调机制: 将内部类,作为参数,作为回调参数传递给。满足条件调用回调函数。
class Main{
public static void main(String[] args) {
Book book = Book.Builder.newBook().name("qhx").price(24);
}
}
class Book {
private String name;
private Integer price;
public Book name(String name){
this.name = name;
return this;
}
public Book price(Integer price){
this.price = price;
return this;
}
static class Builder{
public static Book newBook(){
return new Book();
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getPrice() {
return price;
}
public void setPrice(Integer price) {
this.price = price;
}
}
4.原型模式
理解: 原型模式,就是对一个对象进行深层拷贝而已(对于对象来说,需要创建一个新对象。)。
1.比如,这个对象创建很复杂,但是我们又要用这个对象,所以直接深拷贝。
2.或者这个对象,需要多次用到,多次创建又很麻烦。
关于原型模式,是否能提升速率,那应该跟深拷贝的方法有关。
1.直接去 new对象,然后进行手动拷贝...,或者通过反射api来进行Bean拷贝。
2.将该对象通过序列化和反序列化的形式。
Book book = Book.Builder.newBook().name("qhx").price(24);
ByteArrayOutputStream bom = new ByteArrayOutputStream();
ObjectOutputStream oom = new ObjectOutputStream(bom);
oom.writeObject(book); // 写进去一个对象
ByteArrayInputStream bim = new ByteArrayInputStream(bom.toByteArray());
ObjectInputStream oim = new ObjectInputStream(bim);
Book book1 = (Book) oim.readObject();
5.适配器模式
适配器: 用于将用户希望调用的接口,实际上调用的是另一个接口。
目标接口: 定义客户端所期望的接口
适配器: 实现目标接口,并将请求转换为被适配的调用
被适配者: 需要被适配的对象
5.1 类适配器
PS : 看下面,适配器对象实现目标接口,继承了被适配对象。
这样我们调用目标接口的功能,然后实际上调用的是我们继承被适配对象的功能。
// 目标接口
interface AudioPlayer {
public void play(String audioType, String fileName);
}
// 被适配对象
class Mp3Player {
public void playMp3(String fileName) {
System.out.println("Playing mp3 file: " + fileName);
}
}
// 适配器类
class Mp3PlayerAdapter extends Mp3Player implements AudioPlayer{
@Override
public void play(String audioType, String fileName) {
if(audioType.equalsIgnoreCase("mp3")){
this.playMp3(fileName);
}
}
}
5.2 对象适配器
PS: 看下面,适配器对象已经持有被适配对象,这样我们调用目标接口功能,实际上调用被适配对象的功能。
// 目标接口
interface AudioPlayer {
public void play(String audioType, String fileName);
}
// 被适配对象
class Mp4Player{
public void playMp4(String fileName){
System.out.println("Playing mp4 file:" +fileName);
}
}
// 适配器
class Mp4PlayerAdapter implements AudioPlayer{
private Mp4Player mp4Player;
public Mp4PlayerAdapter(Mp4Player mp4Player) {
this.mp4Player = mp4Player;
}
@Override
public void play(String audioType, String fileName) {
if(audioType.equalsIgnoreCase("map")){
mp4Player.playMp4(fileName);
}
}
}
5.3 接口适配器
PS: 当不需要实现一个接口的全部方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求。
// 3.接口适配器模式
// 目标接口
interface EventListener {
public void onEvent1();
public void onEvent2();
public void onEvent3();
}
// 适配器
abstract class EventListenerAdapter implements EventListener {
@Override
public void onEvent1() {}
@Override
public void onEvent2() {}
@Override
public void onEvent3() {}
}
// 具体适配器模式
class MouseEventListener extends EventListenerAdapter {
@Override
public void onEvent2() {
System.out.println("Mouse clicked");
}
}
6.装饰者模式
理解:
跟代理模式有点类似,但是跟多强调的是对被装饰者功能的扩展。
代理模式:引入一个代理对象控制对目标对象的访问,主要强调的访问控制和管理,如传入参数和返回值的管理。
组成:
Component(抽象组件):定义了被装饰对象和装饰对象的共同接口。
Concrete Component(具体组件):实现了抽象组件接口,是被装饰的对象。
Decorator(装饰者):实现了抽象组件接口,并持有一个抽象组件对象的引用,可以动态地给组件添加额外的行为。
Concrete Decorator(具体装饰者):具体装饰者是具体的装饰对象,通过扩展装饰者的功能给被装饰对象添加新的行为。
// 抽象组件
interface Student{
void run();
}
// 被装饰者
class Boy implements Student{
@Override
public void run() {
System.out.println("男孩跑!");
}
}
// 抽象装饰者
abstract class StudentPlus implements Student{
protected Student student;
public StudentPlus(Student student){
this.student = student;
}
@Override
public void run() {
student.run();
}
}
// 具体装饰者
class BoyPlus extends StudentPlus{
public BoyPlus(Student student) {
super(student); // 抽象类,无法直接new,但是有构造器,被继承new
}
@Override
public void run() {
student.run();
bike();
}
private void bike(){
System.out.println("骑自行车跑!");
}
}
7.代理模式
理解:
代理对象和被代理对象实现同一个接口,我们调用代理对象实际上调用
被代理对象,我们实际通过代理对象对被代理对象做了一个管控,通过代理对象的接口间接调用被代理对象的接口,完成对调用参数的过滤和对响应参数管控或者扩展其他功能。
组成:
抽象主题(Subject):定义了目标对象和代理对象的公共接口,客户端可以通过该接口访问目标对象或代理对象。
目标对象(Real Subject):定义了代理对象所代表的真实对象,客户端最终希望访问的就是该对象。
代理对象(Proxy):持有一个指向目标对象的引用,并实现了与目标对象相同的接口,它可以在调用目标对象之前或之后执行一些额外的逻辑。
7.1 静态代理
我们要实现被代理对象的接口,每次针对被代理对象都需要创建一个代理对象。
public class ProxyMode {
public static void main(String[] args) {
Service serviceProxy = new ServiceProxy(new ServiceImpl());
serviceProxy.buyHouse();
}
}
// 服务接口
interface Service{
void buyHouse();
}
// 服务类
class ServiceImpl implements Service{
@Override
public void buyHouse() {
System.out.println("买房时");
}
}
// 代理类
class ServiceProxy implements Service{
private Service service;
public ServiceProxy(Service service) {
this.service = service;
}
@Override
public void buyHouse() {
System.out.println("买房前!");
service.buyHouse();
System.out.println("买房后!");
}
}
7.2 动态代理
我们不用去实现代理对象,jvm在内存里,帮我们创建代理对象...
实际上,我们这种能传进任意对象,进行代理。
ServiceImpl serviceImpl = new ServiceImpl();
Service serviceProxy1 = (Service)Proxy.newProxyInstance(Service.class.getClassLoader(),
new Class[]{Service.class}, new ServiceProxyHandler(serviceImpl)); // 代理处理器
serviceProxy1.buyHouse(); // method
// 服务接口
interface Service{
void buyHouse();
}
// 服务类
class ServiceImpl implements Service{
@Override
public void buyHouse() {
System.out.println("买房时");
}
}
// 动态代理: 通过java的反射api,在jvm内存里面创建java代理对象,但是我们要实现一个代理类。
class ServiceProxyHandler implements InvocationHandler {
private Object object;
public ServiceProxyHandler(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("买房前!");
Object result = method.invoke(object, args);
System.out.println("买房后!");
return result;
}
}
8. 外观模式
PS :跟mvc三层架构比较像诶。
视图层(View)类似于外观模式中的客户端(Client),它负责向用户展示界面,并接收用户的操作。
控制器层(Controller)类似于外观模式中的外观类(Facade),它负责处理用户的操作并调用相应的业务逻辑。
模型层(Model)则类似于外观模式中的子系统(Subsystems),它包含了应用的业务逻辑和数据访问接口。
理解:
假如说,我们要操作某银行系统,实现一个效果:存钱,打开账户,输入存入金额,点击存钱...这个方法是不是很多..
因此,我们可以新建一个外观类,把这些接口统统封装,并对外暴露一个接口存钱接口,用户通过客户端类,调用这个接口,传入参数就能达到这个效果。
层级: 系统类 -> 外观类 -> 客户端类。
组成:
外观类(Facade):外观类是外观模式的核心,它提供了一个简单的接口,用来访问子系统中的一群接口。外观类将客户端与子系统之间的复杂交互关系封装在内部,对客户端隐藏了子系统的复杂性。
子系统(Subsystems):子系统是指外观类所封装的一组接口,用来实现某个复杂的功能。子系统可以包含多个类,但对于客户端来说,只需要知道外观类提供的简单接口即可。
客户端(Client):客户端是调用外观类的代码,它通过外观类提供的简单接口来和系统进行交互。
// 子系统A
class SubSystemA {
public void operationA() {
System.out.println("SubSystemA: operationA");
}
}
// 子系统B
class SubSystemB {
public void operationB() {
System.out.println("SubSystemB: operationB");
}
}
// 外观类
class Facade {
private SubSystemA subSystemA;
private SubSystemB subSystemB;
public Facade() {
subSystemA = new SubSystemA();
subSystemB = new SubSystemB();
}
public void operation() {
subSystemA.operationA();
subSystemB.operationB();
}
}
// 客户端
class Client {
public static void main(String[] args) {
Facade facade = new Facade();
facade.operation();
}
}
9. 桥接模式
理解:
一个系统有多个功能,每个功能都有各自的接口/抽象类和他们的实现类/继承类,他们之间通过聚合的方式在一起,组成完整的一个系统: 桥接模式是他们直接的具体实现。
组成:
Abstraction(抽象类):定义抽象类的接口,维护一个指向Implementor的指针(引用实例)。
RefinedAbstraction(扩充抽象类):扩展抽象类接口,通常通过对抽象类进行子类化来实现[就是抽象类的实现类]。
Implementor(实现类接口):定义实现类的接口,这个接口不一定要与Abstraction的接口完全一致。实际上这两个接口可以完全不同。
ConcreteImplementor(具体实现类):实现Implementor接口,提供具体的实现。
举例:
下面的手机和软件之间的关系。手机有不同的品牌,软件也有不同;
他们的种类各自有个抽象类/接口,Software有个run()方法,然后AppStore和Wx分别有他们的run(),
我们在Phone的抽象类中,拿到了Softaware的引用,并且我能通过setSoftware()传进不同的software实现类,来达到启动不同软件的效果。
Phone apple = new Apple();
apple.setSoftware(new Wx());
Apple apple1 = new Apple();
apple1.setSoftware(new AppStore());
interface Software{
void run();
}
class AppStore implements Software{
@Override
public void run() {
System.out.println("商店启动!");
}
}
class Wx implements Software{
@Override
public void run() {
System.out.println("微信启动!");
}
}
abstract class Phone{
protected Software software;
public void setSoftware(Software software) {
this.software = software;
}
abstract public void run();
}
class Apple extends Phone{
@Override
public void run() {
software.run();
}
}
10. 组合模式
理解:
组合结构中的类,是整体和部分之间的关系。如: 文件(叶节点)和文件夹(容器节点), 文件夹里面能包含文件和文件夹。
组成:
- 组件(Component):定义了组合中对象的通用接口,可以是抽象类或接口。它声明了一些操作方法,如添加、删除、获取子组件等,这些方法可以有默认实现。
- 叶节点(Leaf):代表组合中的叶子对象,它没有子组件。叶节点是组合结构的最基本单元,通常实现组件接口的方法。
- 容器节点(Composite):代表组合中的容器对象,它可以包含其他子组件。容器节点实现了组件接口,并提供了添加、删除、获取子组件等方法。
// 文件夹 - 文件之间: 表示整体和部分之间。 一个文件夹,里面包括多个文件
// 客户端可以一致的对待单个对象和组合对象,因为接口是一样的???
// 扩展很容易: 增加新的组件类型,符合开闭原则,在不用改变现有代码的情况下,能够对
public class CompositeMode {
}
// 组件: 定了组合中对象的通用接口。可以是抽象类或其他接口
interface FileSystemComponent{
void printName();
int getSize();
}
// 文件类: 叶节点,他没有子组件,是组合结构中的基本单元,通常实现组件接口的方法。
class File implements FileSystemComponent{
private String name;
private int size;
@Override
public void printName() {
System.out.println("File:" +name);
}
@Override
public int getSize() {
return size;
}
}
// 文件夹类: 容器节点,代表组合中的容器对象,可以包含其他子组件。容器节点也实现了组件接口,并提供了add/remove/select等方法。
class Folder implements FileSystemComponent{
private String name;
private List<FileSystemComponent> children;
public boolean addComponent(FileSystemComponent fileSystemComponent){
return children.add(fileSystemComponent);
}
public boolean removeComponent(FileSystemComponent fileSystemComponent){
return children.remove(fileSystemComponent);
}
public FileSystemComponent selectComponent(int index){
return children.get(index);
}
public Folder(String name) {
this.name = name;
this.children = children = new ArrayList<>();
}
@Override
public void printName() {
System.out.println("Folder:" +name);
for (FileSystemComponent child : children) {
child.printName();
}
}
@Override
public int getSize() {
int totalSize = 0;
for (FileSystemComponent child : children) {
totalSize += child.getSize();
}
return totalSize;
}
}
11.享元模式
理解:
享元工厂创建享元对象,传入内部状态。享元工厂通过内部状态判断享元对象是否存在,存在不创建,存在创建。根据享元状态创建的享元对象,传入外部参数。
PS: 因为有大量对象,创建太多会占用内存。因此,通过享元模式,我们的将享元对象分为内部状态和外部状态。内部状态,可以理解为是属性;而外部状态,就是通过方法传入的参数之类的。享元工厂负责创建和管理享元对象,通过一个池(集合)存储已经创建好的享元对象,确保对象的共享和复用。
组成:
抽象享元(Flyweight):定义享元对象的接口,声明需要接收外部状态参数的方法。
具体享元(Concrete Flyweight):实现抽象享元接口,存储并管理内部状态。
享元工厂(Flyweight Factory):负责创建和管理享元对象,通过一个池(如集合)来存储已经创建的享元对象,确保对象的共享和复用。
客户端(Client):使用享元对象的客户端,维护外部状态,并将其传入享元对象。
优点:
减少内存使用:通过共享对象的内部状态,减少相同或相似对象的数量,从而减少内存的使用。
提高性能:共享对象可以被多个客户端同时使用,减少了对象的创建和销毁的开销,提高了系统的性能。
简化对象结构:享元模式将对象的状态分为内部状态和外部状态,简化了对象的结构,使得对象更加轻量级。
// 客户端
public class FlyweightMode {
public static void main(String[] args) {
Car car1 = CarFactory.getCar("长安一号", "ChangAn");
Car car2 = CarFactory.getCar("长安二号", "ChangAn");
Car car3 = CarFactory.getCar("长安二号", "ChangAn");
car1.run("小王");
car2.run("小红");
car3.run("小白");
}
}
// 抽象享元
interface Car{
void run(String driver);
}
// 享元实体类
class ChangAn implements Car{
private String carName;
public ChangAn(String carName) {
this.carName = carName;
}
@Override
public void run(String driver) {
System.out.println(driver + "开着," +carName);
}
}
// 享元工厂
class CarFactory{
private static Map<String,Car> carMap = new HashMap();
private CarFactory(){
}
public static Car getCar(String carName,String type){
Car car = carMap.get(carName);
if(car == null){
if(type.equals("ChangAn")){
car = new ChangAn(carName);
carMap.put(carName,car);
}
}
return car;
}
}
12.策略模式
理解:
通过外包一层,隔绝算法的实现和算法的调用。用户,只需要知道怎么使用该接口,并得到什么算法实现的结果。
定义:
它定义了一系列的算法,将每个算法封装起来,并使它们可以互相替换。通过这种方式,客户端程序不需要知道每个算法背后的具体实现,而只需要选择合适的算法并调用。
组成:
策略接口(Strategy Interface):用于定义所有支持的算法的公共接口,客户端通过该接口调用算法。
具体策略类(Concrete Strategy Class):实现策略接口,包含了具体的算法实现逻辑。
环境类(Context Class):包含一个策略对象,并提供一个调用策略的方法,客户端通过环境类来调用不同的算法。
public class StrategyMode {
public static void main(String[] args) {
// 根据跟工厂模式,唯一大的不同,工厂模式,他给我们创建对象;策略模式,是我们自己创建对象
Environment environment = new Environment(new QQStrategy());
environment.writeData();
Environment environment1 = new Environment(new WxStrategy());
environment1.writeData();
}
}
// 策略接口
interface Strategy{
void write();
}
// 具体策略类
class QQStrategy implements Strategy{
@Override
public void write() {
System.out.println("数据写入QQ!");
}
}
class WxStrategy implements Strategy{
@Override
public void write() {
System.out.println("数据写入微信!");
}
}
// 环境类
class Environment {
private Strategy strategy;
public Environment(Strategy strategy) {
this.strategy = strategy;
}
public void writeData(){
strategy.write();
}
}
13.模版模式
理解:
通过父类定义模版方法,定义抽象方法,由子类去实现这些抽象方法,完成算法结构的实现。
组成:
抽象父类(AbstractClass):实现了模板方法,定义了算法的骨架。
具体类(ConcreteClass):实现抽象类中的抽象方法,即不同的对象的具体实现细节。
public class TemplateMode {
public static void main(String[] args) {
People wang = new WangPeople();
wang.run();
}
}
// 抽象父类:定义类的骨架
abstract class People{
void run(){
this.laShen();
this.PaoBu();
}
abstract void laShen();
abstract void PaoBu();
}
// 具体类: 去实现抽象父类的骨架
class WangPeople extends People{
@Override
void laShen() {
System.out.println("小王,拉伸!");
}
@Override
void PaoBu() {
System.out.println("小王跑步!");
}
}
14.观察者模式
理解:
就是当一个被观察者对象的状态发生改变时,会通知其他观察者对象发生改变。
使用场景:
事件驱动编程。观察者模式是事件驱动编程的一种常用实现方式,它通过将 事件源和事件处理分离 ,使得事件源不必知道哪些对象对其状态感兴趣,而只需将事件通知给所有已注册的观察者对象即可。
GUI界面开发。在GUI界面开发中,用户通过某种操作或输入改变了界面上的控件状态,这些状态改变将会被触发并通知给相应的观察者对象,观察者对象根据状态变化来做出相应的更新操作,从而实现界面的实时更新。
消息队列系统。消息队列系统通常需要处理大量的异步消息,这些消息可能来自于不同的发送者,而每个发送者又可能有多个接收者。观察者模式可以帮助消息队列系统管理和分发这些消息,保证它们能够被正确地传递和处理。
数据库连接池。在数据库连接池中,观察者模式可以用来监测数据库连接的状态,当某个连接出现问题时,观察者模式可以及时通知其他连接池对象进行处理,从而保证整个连接池的可用性。
订阅系统。订阅系统是指允许用户订阅某个主题或频道,并接收相关信息或内容的系统。观察者模式可以用来实现订阅系统中的事件通知和内容分发功能,从而使得用户能够及时获取到他们感兴趣的信息或内容。
看下面,假如window系统启动时,通知cpu日志和风扇(fs)日志的观察者打印info信息。// 或者说,一台电脑启动,被CPU和风扇的事件监听器监听,监听到之后通知CPU和风扇启动。
public class ObserverMode {
public static void main(String[] args) {
SystemRunLog systemRunLog1 = new SystemRunLog();
SystemRunLog systemRunLog2 = new SystemRunLog();
WinSystem winSystem = new WinSystem();
winSystem.addLog(systemRunLog1);
winSystem.addLog(systemRunLog2);
winSystem.run();
}
}
// 主题
interface SystemP{
void run();
}
// 具体主题
class WinSystem implements SystemP{
List<Log> list;
public WinSystem() {
this.list = new ArrayList<>();
}
@Override
public void run(){
systemRunInfoLog();
}
private void systemRunInfoLog() {
for (Log log : list) {
log.info("启动-");
}
}
// 负责管理观察者
public boolean addLog(Log log){
return list.add(log);
}
public boolean removeLog(Log log){
return list.remove(log);
}
}
// 观察者
interface Log{
void info(String mes);
void error(String mes);
}
// 具体观察者
class CpuRunLog implements Log{
@Override
public void info(String mes) {
System.out.println("cpu info:" + mes);
}
@Override
public void error(String mes) {
System.out.println("cpu error:" +mes);
}
}
class FsRunLog implements Log{
@Override
public void info(String mes) {
System.out.println("fs info:" + mes);
}
@Override
public void error(String mes) {
System.out.println("fs error:" +mes);
}
}
15.迭代器模式
理解:
提供一种顺序访问聚合对象中各个元素的方法,而不需要暴露聚合对象的内部表示。
什么是聚合对象?
聚合对象 负责存储和管理一组元素,并提供一种方式来获取迭代器对象,从而使得外部客户端可以通过迭代器访问和遍历聚合对象中的元素,而不必了解其内部表示和结构。
适用场景?
集合类:例如列表、数组等需要遍历元素的容器类。
数据库查询结果集:当需要逐行处理数据库查询结果时,可以使用迭代器模式来遍历结果集。
文件解析:当需要逐行读取文件内容时,可以使用迭代器模式来实现逐行解析。
public class IteratorMode {
public static void main(String[] args) {
ArrayList<String> objects = new ArrayList<>();
objects.add("qhx");
objects.add("qhx");
objects.add("qhx");
Aggregate concreteAggregate = new ConcreteAggregate<>(objects);
Iterator<String> iterator = concreteAggregate.createIterator();
while (iterator.hasNext()){
Object next = iterator.next();
System.out.println(next);
}
}
}
// 迭代器接口
interface Iterator<T>{
T next();
boolean hasNext();
}
// 具体迭代器
class ConcreteIterator<T> implements Iterator<T>{
private List<T> list;
private int index;
public ConcreteIterator(List<T> list) {
this.list = list;
this.index = 0;
}
@Override
public T next() {
if(hasNext()){
return list.get(index++);
}
return null;
}
@Override
public boolean hasNext() {
return index < list.size();
}
}
// 聚合类
interface Aggregate {
Iterator createIterator();
}
// 具体聚合类
class ConcreteAggregate<T> implements Aggregate {
private List<T> list;
public ConcreteAggregate(List<T> list) {
this.list = list;
}
@Override
public Iterator createIterator() {
return new ConcreteIterator(list);
}
}
16.责任链模式
理解:
允许多个对象按照顺序处理请求。在这个模式中,每个对象都有机会处理请求,可以选择自己处理或者将其传递给链中的下一个对象。这样,请求就能在一系列对象之间传递,直到有一个对象处理它为止。也就是多个对象成链状组装在一起,共同处理事务,当满足条件的事务,对象就会处理,不满足就向下一个对象传递。
组成:
抽象处理者(Handler):定义一个处理请求的接口,并包含一个对下一个处理者的引用。它可以是抽象类或接口,规范处理者的行为。
具体处理者(Concrete Handler):继承自抽象处理者,实现处理请求的方法。每个具体处理者都会根据自己的能力来判断是否能够处理该请求,如果能够处理,则进行处理;否则将请求传递给下一个处理者。
客户端(Client):创建处理者对象并组成责任链,将请求发送给链的第一个处理者。
//假设我们有一个购买审批系统,需要按照不同的金额级别对购买请求进行审批。
public class ResponsibilityChainMode {
public static void main(String[] args) {
ClientL clientL = new ClientL();
clientL.sendRequest(5000);
}
}
// 客户端:负责用户发送请求
class ClientL {
private Handler firstHandler;
public ClientL() { // 创建客户端时,就初始化责任链()
this.firstHandler = new ManagerHandler();
Handler handler2 = new DirectorHandler();
firstHandler.setNextHandler(handler2);
}
// 发送请求
void sendRequest(int amount){
firstHandler.handlerRequest(new PurchaseRequest(amount));
}
}
// 抽象处理着:拿到下个处理着的引用、处理请求的方法
abstract class Handler{
public Handler nextHandler;
public void setNextHandler(Handler nextHandler) {
this.nextHandler = nextHandler;
}
public abstract void handlerRequest(PurchaseRequest request);
}
// 具体处理着:经理
class ManagerHandler extends Handler{
@Override
public void handlerRequest(PurchaseRequest request) {
if(request.amount <= 1000){
System.out.println("Manager 可以处理!");
}else if(nextHandler != null){ // 传给
nextHandler.handlerRequest((request));
}
}
}
// 具体处理着:管理者
class DirectorHandler extends Handler{
@Override
public void handlerRequest(PurchaseRequest request) {
if(request.amount <= 5000){
System.out.println("Director 可以处理!");
}else if(nextHandler != null){ // 传给
nextHandler.handlerRequest((request));
}
}
}
// 请求
class PurchaseRequest {
Integer amount; // 金额
public PurchaseRequest(Integer amount) {
this.amount = amount;
}
}
17.命令模式
组成:
命令(Command):定义了执行操作的接口,包括一个执行方法(通常为
execute()
具体命令(Concrete Command):实现了命令接口,将一个接收者对象绑定到一个动作,并在执行方法中调用接收者的相关操作。
接收者(Receiver):执行实际操作的对象。命令对象将请求委派给接收者来执行具体的操作。
调用者(Invoker):持有命令对象并调用其执行方法来触发请求操作。
客户端(Client):创建具体命令对象,并将其与接收者关联起来,然后将命令对象交给调用者进行处理。
ps:行为请求者不应该依赖于单个具体的行为实现者,而是应该通过抽象接口或中介对象来间接地调用实现者。
举例:
假设我们有一个家电控制系统,其中包括电视、音响和灯光等设备。现在我们希望实现一个遥控器,可以通过按钮来控制不同的设备,例如打开电视、关闭音响、调暗灯光等操作。
public class CommandMode {
public static void main(String[] args) {
RemoteControl remoteControl = new RemoteControl();
TVCommand tvCommand = new TVCommand();
TV tv = new TV();
tvCommand.setTv(tv);
remoteControl.setCommand(tvCommand);
remoteControl.pressButton();
}
}
// 定义一个命令抽象类
interface Command {
void execute();
}
// 针对电视设备的具体命令类。
class TVCommand implements Command{
private TV tv;
public void setTv(TV tv) {
this.tv = tv;
}
@Override
public void execute() {
tv.run();
}
}
// 电视设备(命令接受者)
class TV {
public void run(){
System.out.println("电视启动!");
}
}
// 电视遥控器(命令调用者)
class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton(){
command.execute();
}
}
好像明白了,命令模式,还是遵循一个调用实例拿到一个抽象对象的引用。
只不过,你看上述,设备抽象接口化好像不太合适,因此中间又加了一层命令抽象化,电视控制器调用具体的命令实现。命令实现拿着具体设备的引用。
进而 遥控器 -> 命令 -> 设备。
18.状态模式
组成:
Context(上下文):上下文是一个包含状态对象的类,它可以 调用状态对象的方法,并在状态对象内部状态发生改变时更新自身的状态。
State(状态):状态是一个接口或抽象类,定义了具体状态需要实现的方法。具体的状态类通过继承和实现来实现这些方法。
Concrete State(具体状态):具体状态是状态接口的不同实现,每个具体状态都有自己的行为。
理解:
上下文对象持有状态类,而上下文的行为,依赖于状态类(属性),状态的改变会影响上下文对象的行为改变。
PS:也就是说跟策略模式的实现逻辑类似,只不过客观主体不同?那个在于能调用不同的策略,而这个在于包装对象的行为,依赖于不同状态对象的行为。
public class StateMode {
public static void main(String[] args) {
Order order = new Order(new PendingPaymentState());
order.processOrder(); // 输出:订单待支付
order.setState(new PaidState());
order.processOrder(); // 输出:订单已支付
order.setState(new ShippedState());
order.processOrder(); // 输出:订单已发货
}
}
// 状态接口
interface State {
void handle();
}
// 具体状态类:待支付状态
class PendingPaymentState implements State {
public void handle() {
System.out.println("订单待支付");
// TODO: 处理待支付状态的逻辑
}
}
// 具体状态类:已支付状态
class PaidState implements State {
public void handle() {
System.out.println("订单已支付");
// TODO: 处理已支付状态的逻辑
}
}
// 具体状态类:已发货状态
class ShippedState implements State {
public void handle() {
System.out.println("订单已发货");
// TODO: 处理已发货状态的逻辑
}
}
// 上下文类:订单
class Order {
private State state;
public Order(State state) {
this.state = state;
}
public void setState(State state) {
this.state = state;
}
public void processOrder() {
state.handle();
}
}
19.备忘录(快照)模式
理解:
有时候需要记录某个时刻的状态,并且在之后去恢复这个状态。
也就是说,备忘录模式提供了一种状态恢复的一种方案。
PS:遵循单一责任原则,每个类根据责任划分,符合解耦,而且这样代码阅读性比较高。
组成:
发起人(Originator):它是 拥有内部状态的对象,需要保存和恢复状态 。它提供了创建备忘录对象以及从备忘录对象中恢复状态的方法。
备忘录(Memento):它是用于存储发起人对象内部状态的对象。备忘录可以包含发起人对象的部分或全部状态信息。
管理者(Caretaker):它负责存储备忘录对象,以便在需要时进行检索和管理。
案列:
public class MementoMode {
public static void main(String[] args) {
TextEditor editor = new TextEditor();
TextStateManager stateManager = new TextStateManager();
LinkedList<Object> objects = new LinkedList<>();
editor.setText("Hello, World!"); // 设置文本
stateManager.saveState(editor); // 保存状态
editor.setText("Hello, GPT-3.5!"); // 修改文本
System.out.println(editor.getText()); // 输出:Hello, GPT-3.5!
stateManager.restoreState(editor); // 恢复状态
System.out.println(editor.getText()); // 输出:Hello, World!
}
}
// 发起人(文本编辑器类)
class TextEditor{
private String text;
public void setText(String text) {
this.text = text;
}
public String getText() {
return text;
}
public void restore(Memento memento) {
text = memento.getText();
}
}
// 备忘录(文本状态类)
class Memento {
private String text;
public Memento(String text) {
this.text = text;
}
public String getText() {
return text;
}
}
// 管理者(文本状态管理类)
class TextStateManager {
private Stack<Memento> stack = new Stack<>();
public void saveState(TextEditor textEditor) {
stack.push(new Memento(textEditor.getText()));
}
public void restoreState(TextEditor textEditor) {
if (!stack.isEmpty()) {
textEditor.restore(stack.pop());
}
}
}
20.访问者模式
理解:
针对访问者对不同元素的访问,提供给了不同代码操作;因此,实际上我们考虑访问者(用户调用)和被访问元素类的解耦。
组成:
抽象访问者(Visitor):定义了对每个具体元素(ConcreteElement)访问时所产生的具体行为。它包含了访问者的核心逻辑,即具体业务方法,用于访问并处理不同类型的元素对象。
具体访问者(ConcreteVisitor):实现了抽象访问者(Visitor)所定义的接口,即定义了对每个具体元素(ConcreteElement)访问时所产生的具体行为。它通过重写抽象访问者中的具体业务方法,实现了对不同类型的元素对象的访问和处理。
抽象元素(Element):定义了一个accept()方法,该方法接受一个访问者(Visitor)作为参数,并调用该访问者的visit()方法来实现对该元素的访问。它是被访问的对象的抽象表示,其中包含了具体访问者所需要的数据。
具体元素(ConcreteElement):实现了抽象元素(Element)所定义的接口,即实现了accept()方法。它是被访问的对象的具体表示,其中包含了具体访问者所需要的数据。
对象结构(Object Structure):定义了一个容纳元素对象的集合,并提供了一些基本操作,如添加、删除、遍历等。它可以是一个复杂的数据结构或一个简单的集合,其中包含了各种类型的元素对象。
实例:
public class VisitorMode {
public static void main(String[] args) {
// 但是,针对每种设计模式,我才遵循思想,不遵循绝对设计原则
ConcreteVisitor concreteVisitor = new ConcreteVisitor();
ConcreteElementA concreteElementA = new ConcreteElementA();
concreteElementA.accept(concreteVisitor);
// 针对访问了不同元素,提供了不同的行为逻辑。
}
}
// 抽象访问者:定义了针对某个具体元素访问所产生的具体行为
interface Visitor {
void visit(ConcreteElementA element);
void visit(ConcreteElementB element);
}
// 具体访问者
class ConcreteVisitor implements Visitor {
@Override
public void visit(ConcreteElementA element) {
System.out.println("Visitor is visiting ConcreteElementA");
// 对ConcreteElementA进行具体的操作
}
@Override
public void visit(ConcreteElementB element) {
System.out.println("Visitor is visiting ConcreteElementB");
// 对ConcreteElementB进行具体的操作
}
}
// 抽象元素: 定义了一个accept()方法,该方法以访问者作为参数,调用visitor方法来完成元素的访问。
interface Element{
void accept(Visitor visitor);
}
// 具体元素A
class ConcreteElementA implements Element{
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 具体元素B
class ConcreteElementB implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 对象结构:定义了一个容纳元素对象的集合
class ObjectStructure {
private List<Element> elements = new ArrayList<>();
public void addElement(Element element) {
elements.add(element);
}
public void removeElement(Element element) {
elements.remove(element);
}
public void accept(Visitor visitor) {
for (Element element : elements) {
element.accept(visitor);
}
}
}
21.中介者模式
// 谈谈我对集中处理和分布式处理的理解,集中处理的好处是,所有文件集中配置,肯定好管理,但是一旦项目大了,需要集中的东西业也越来越多,和这个时候,我们再集中处理,代码可读性不高,因此我们可以按照功能、业务划分模块,在这个模块,我们再集中一起处理,这样代码可读性明显更高!!
为服务,就是在此基础上形成的,代码量变大,针对每个功能模块单独成分成一个模块(一般是直接打包成为一个服务!)。
理解:
中介者模式适用于对象之间存在复杂的交互关系,且希望减少对象之间的直接耦合的情况。通过引入中介者对象,可以将对象之间的交互逻辑进行集中和封装,提高系统的灵活性和可维护性。
一群对象,通过中介者类进行互相交通信?
组成:
中介者(Mediator)
:定义了对象之间的协调和通信接口,通常包含多个通信方法,用于处理不同的交互场景。中介者对象持有对各个对象的引用,负责将接收到的请求转发给合适的对象进行处理。
具体中介者(Concrete Mediator)
:实现了中介者接口,负责实际的协调和通信逻辑。它通常需要了解并持有对各个相关对象的引用,以便根据不同的交互情况进行适当的分发和转发。
同事类(Colleague)
:表示一个参与交互的对象,每个同事类都知道中介者对象,并且与其他同事类通过中介者进行通信。同事类之间可以有不同的实现,但它们之间不直接交互,而是通过中介者进行间接通信。
具体同事类(Concrete Colleague)
:实现了同事类接口,负责实际的业务逻辑,并通过中介者对象来与其他同事类进行通信和协调。
代码:
public class MediatorMode {
public static void main(String[] args) {
// 用户先注册聊天室
ChatRoomMediator chatRoom = new ChatRoom();
User qhx = new User("qhx", chatRoom);
User wls = new User("wls", chatRoom);
User wxh = new User("wxh", chatRoom);
chatRoom.addUser(qhx);
chatRoom.addUser(wls);
chatRoom.addUser(wxh);
qhx.send("hello!");
}
}
// 中介者接口
interface ChatRoomMediator {
public void sendMessage(String message, User user);
public void addUser(User user);
}
// 具体中介者
class ChatRoom implements ChatRoomMediator {
private List<User> users;
public ChatRoom() {
this.users = new ArrayList<>();
}
@Override
public void sendMessage(String message, User user) {
for (User u : users) {
if (u != user) {
u.receive(message);
}
}
}
@Override
public void addUser(User user) {
this.users.add(user);
}
}
// 同事类
class User {
private String name;
private ChatRoomMediator chatRoom;
public User(String name, ChatRoomMediator chatRoom) {
this.name = name;
this.chatRoom = chatRoom;
}
public void send(String message) {
chatRoom.sendMessage(message, this);
}
public void receive(String message) {
System.out.println(name + " received message: " + message);
}
}