设计模式
设计模式是前辈们对代码开发经验的总结,是解决特定问题的一系列的套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。
学习设计模式的意义
设计模式的本质是面向对象设计原则的实际应用,是对类的封装性、继承性和多态性以及类的关联关系的组合关系的充分理解。
正确使用设计模式具有一下优点:
- 可以提高程序员的思维能力、编程能力和设计能力
- 是程序设计更加标准化、代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期
- 使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强
设计模式的分类
- 创建型模式:
- 单例模式、工厂模式、抽象工厂模式、创造者模式、原型模式
- 结构型模式:
- 适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式
- 行为型模式
- 模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式
OOP七大原则
- 开闭原则: 对扩展开放,对修改关闭
- 里氏替换原则: 继承必须确保超类所拥有的性质在子类中仍然成立
- 依赖导致原则: 要面向接口编程,不要面向实现编程
- 单一职责原则: 控制类的粒度大小、将对象解耦、提高其内聚性
- 接口隔离原则: 要为各个类建立他们需要的专用接口
- 迪米特法则: 只与你的直接朋友交谈,不跟”陌生人”说话
- 合成复用原则: 尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现
1.Java类加载机制
类加载的时机
- 隐式加载 new 创建类的实例,
- 显式加载:loaderClass,forName等
- 访问类的静态变量,或者为静态变量赋值
- 调用类的静态方法
- 使用反射方式创建某个类或者接口对象的Class对象。
- 初始化某个类的子类
- 直接使用
java.exe
命令来运行某个主类
类加载的过程
加载–》验证–》准备–》解析–》初始化
加载
类加载过程的一个阶段,ClassLoader通过一个类的完全限定名查找此类字节码文件,并利用字节码文件创建一个class对象。
验证
目的在于确保class文件的字节流中包含信息符合当前虚拟机要求,不会危害虚拟机自身的安全,主要包括四种验证:文件格式的验证,元数据的验证,字节码验证,符号引用验证。
准备
为类变量(static修饰的字段变量)分配内存并且设置该类变量的初始值,(如static int i = 5 这里只是将 i 赋值为0,在初始化的阶段再把 i 赋值为5),这里不包含final修饰的static ,因为final在编译的时候就已经分配了。这里不会为实例变量分配初始化,类变量会分配在方法区中,实例变量会随着对象分配到Java堆中
解析
这里主要的任务是把常量池中的符号引用替换成直接引用
初始化
这里是类记载的最后阶段,如果该类具有父类就进行对父类进行初始化,执行其静态初始化器(静态代码块)和静态初始化成员变量。(前面已经对static 初始化了默认值,这里我们对它进行赋值,成员变量也将被初始化)
2.单例模式
特点:保证一个类只有一个实例,并且提供一个全局访问点
2.1懒汉模式
懒汉模式:延迟加载, 只有在真正使用的时候,才开始实例化。
问题:
- 线程不安全
- 指令重排
package com.wdzl;
/**
* 懒汉模式
*/
public class LazySingleton {
private LazySingleton() {
}
private volatile static LazySingleton instance;
/**
* 双重锁
*
* @return
*/
public static LazySingleton getInstance() {
if (instance == null) {
synchronized (LazySingleton.class) {
if (instance == null) {
instance = new LazySingleton();
// 字节码层,创建对象
// JIT , CPU 有可能对如下指令进行重排序
// 1 .分配空间
// 2 .初始化
// 3 .引用赋值
}
}
}
return instance;
}
}
2.2饿汉模式
饿汉模式:类加载的 初始化阶段就完成了 实例的初始化 。
本质上就是借助于jvm 类加载机制,保证实例的唯一性(初始化过程只会执行一次)及线程安 全(JVM以同步的形式来完成类加载的整个过程)。
package com.wdzl;
/**
* 饿汉模式
*/
public class HungrySingleton {
private HungrySingleton(){
}
private static HungrySingleton instance=new HungrySingleton();
public static HungrySingleton getInstance(){
return instance;
}
}
2.3静态内部类
1).本质上是利用类的加载机制来保证线程安全
2).只有在实际使用的时候,才会触发类的初始化,所以也是懒加载的一 种形式。
package com.wdzl;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class InnerClassSingleton {
private InnerClassSingleton(){
}
private static class InnerClassHolder{
private static InnerClassSingleton instance=new InnerClassSingleton();
}
public static InnerClassSingleton getInstance(){
return InnerClassHolder.instance;
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//反射创建对象
Constructor<InnerClassSingleton> declaredConstructor = InnerClassSingleton.class.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
InnerClassSingleton innerClassSingleton = declaredConstructor.newInstance();
//调用方法创建对象
InnerClassSingleton instance = InnerClassSingleton.getInstance();
System.out.println(innerClassSingleton==instance);
}
}
静态内部类防止反射破坏
private InnerClassSingleton(){
if (InnerClassHolder.instance!=null){
throw new RuntimeException("单例不允许有多个实例");
}
}
2.4枚举类型
1)天然不支持反射创建对应的实例,且有自己的反序列化机制
2)利用类加载机制保证线程安全
可以发现是因为EnumSingleton.class.getDeclaredConstructors()获取所有构造器,会发现并没有我们所设置的无参构造器,只有一个参数为(String.class,int.class)构造器,然后看下Enum源码就明白,这两个参数是name和ordial两个属性:
package com.wdzl;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public enum EnumSingleton {
INSTANCE;
public void print(){
System.out.println(this.hashCode());
}
}
class Demo{
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
EnumSingleton instance = EnumSingleton.INSTANCE;
EnumSingleton instance2 = EnumSingleton.INSTANCE;
instance.print();
instance2.print();
System.out.println(instance==instance2);
Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
EnumSingleton enumSingleton = declaredConstructor.newInstance();
}
}
3.工厂模式
核心本质:
- 实例化对象不适用new, 用工厂方法代替
- 将选择实现类,创建对象统一管理和控制,从而将调用者跟我们的实现类解耦
三种模式:
- 简单工厂模式
- 用来生产同一等级结构中的任何产品(对于增加新的产品,需要扩展已有代码)
- 工厂方法模式
- 用来生产同一等级结构中的固定产品(支持增加任意产品)
- 抽象工厂模式
- 围绕一个超级工厂创建其他工厂,该超级工厂又被称为其他工厂的工厂
3.1简单工厂模式
1.定义一个接口
package com.wdzl.fatory.simple;
public interface Car {
void getName();
}
2.接口实现类
public class Tesl implements Car {
@Override
public void getName() {
System.out.println("特斯拉。。。");
}
}
public class WuLing implements Car{
@Override
public void getName() {
System.out.println("五菱宏光。。。");
}
}
public class R8 implements Car{
@Override
public void getName() {
System.out.println("奥迪R8.。。。");
}
}
3.简单工厂
package com.wdzl.fatory.simple;
//静态工厂模式
public class CarFactory {
// 方法一 违反了开闭原则
public static Car getCar(String car){
if ("五菱宏光".equals(car)){
return new WuLing();
}else if ("特斯拉".equals(car)){
return new Tesl();
}else{
return new Car() {
@Override
public void getName() {
System.out.println("没这车");
}
};
}
}
// 方法二
public static Car getWuLing(){
return new WuLing();
}
public static Car getTesl(){
return new Tesl();
}
public Car getR8(){
return new R8();
}
}
4.消费者(输出)
package com.wdzl.fatory.simple;
public class Consumer {
public static void main(String[] args) {
//正常方法
Car car=new WuLing();
Car car2=new Tesl();
car.getName();
car2.getName();
//使用工厂创建 方法一
Car wl=CarFactory.getCar("五菱宏光");
wl.getName();
Car tl=CarFactory.getCar("特斯拉");
tl.getName();
//方法二
Car wl2=CarFactory.getWuLing();
wl2.getName();
Car tl2=CarFactory.getTesl();
tl2.getName();
/**
* 不使用静态方法
*/
CarFactory carFactory = new CarFactory();
Car r8 = carFactory.getR8();
r8.getName();
}
}
3.2工厂方法模式
接口与接口的实现类与简单工厂模式定义相同
1.创建工厂方法
public interface CarFactory {
Car getCar();
}
public class TeslFactory implements CarFactory{
@Override
public Car getCar() {
return new Tesl();
}
}
public class R8Factory implements CarFactory{
@Override
public Car getCar() {
return new R8();
}
}
public class WuLingFactory implements CarFactory{
@Override
public Car getCar() {
return new WuLing();
}
}
2.输出
package com.wdzl.fatory.method;public class Consumer { public static void main(String[] args) { Car car=new WuLingFactory().getCar(); Car car2=new TeslFactory().getCar(); car.getName(); car2.getName(); new R8Factory().getCar().getName(); }}
简单工厂与工厂方法结论
-
结构复杂度: simple
-
代码复杂度: simple
-
编程复杂度: simple
-
管理上的复杂度: simple
根据设计原则: 工厂方法模式
根据实际业务: 简单工厂模式
3.3.抽象工厂
1.定义一个产品工厂
//抽象产品工厂
public interface IProductFactory {
// 生产手机
PhoneProduct phoneProduct();
// 生产路由器
RouterProduct routerProduct();
}
2.定义产品接口
//手机接口
public interface PhoneProduct {
void start();
void shutdown();
void callup();
void sendSMS();
}
//路由器接口
public interface RouterProduct {
void start();
void shutdown();
void openWifi();
void setting();
}
3.产品实现类
public class HuaweiPhone implements PhoneProduct{
@Override
public void start() {
System.out.println("打开华为手机");
}
@Override
public void shutdown() {
System.out.println("关闭华为手机");
}
@Override
public void callup() {
System.out.println("用华为手机打电话");
}
}
@Override
public void sendSMS() {
System.out.println("用华为手机发短信");
}
public class HuaweiRouter implements RouterProduct{
@Override
public void start() {
System.out.println("打开华为路由器");
}
@Override
public void shutdown() {
System.out.println("关闭华为路由器");
}
@Override
public void openWifi() {
System.out.println("打开华为wifi");
}
@Override
public void setting() {
System.out.println("设置华为WiFi");
}
public class XiaoMiPhone implements PhoneProduct {
@Override
public void start() {
System.out.println("打开小米手机");
}
@Override
public void shutdown() {
System.out.println("关闭小米手机");
}
@Override
public void callup() {
System.out.println("用小米手机打电话");
}
@Override
public void sendSMS() {
System.out.println("用小米手机发短信");
}
}
public class XiaoMiRouter implements RouterProduct {
@Override
public void start() {
System.out.println("打开小米路由器");
}
@Override
public void shutdown() {
System.out.println("关闭小米路由器");
}
@Override
public void openWifi() {
System.out.println("打开小米wifi");
}
4.创建产品工厂
public class HuaWeiFactory implements IProductFactory {
@Override
public PhoneProduct phoneProduct() {
return new HuaweiPhone();
}
@Override
public RouterProduct routerProduct() {
return new HuaweiRouter();
}
}
public class XiaoMiFactory implements IProductFactory{
@Override
public PhoneProduct phoneProduct() {
return new XiaoMiPhone();
}
@Override
public RouterProduct routerProduct() {
return new XiaoMiRouter();
}
}
5.输出
public class Client {
public static void main(String[] args) {
System.out.println("====================华为产品====================");
HuaWeiFactory huaweiFactory = new HuaWeiFactory();
PhoneProduct huaweiPhone = huaweiFactory.phoneProduct();
huaweiPhone.start();
huaweiPhone.callup();
huaweiPhone.sendSMS();
huaweiPhone.shutdown();
RouterProduct huaweiRouter = huaweiFactory.routerProduct();
huaweiRouter.start();
huaweiRouter.openWifi();
System.out.println("====================小米产品====================");
XiaoMiFactory xiaomiFactory = new XiaoMiFactory();
PhoneProduct xiaomiPhone = xiaomiFactory.phoneProduct();
xiaomiPhone.start();
xiaomiPhone.callup();
xiaomiPhone.sendSMS();
xiaomiPhone.shutdown();
RouterProduct xiaomiRouter = xiaomiFactory.routerProduct();
xiaomiRouter.start();
xiaomiRouter.openWifi();
}
}
结论:
抽象工厂模式
-
定义: 抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定他们具体的类
-
适用场景:
- 客户端(应用层) 不依赖与产品类实例如何被创建、实现等细节
- 强调一系列相关的产品对象(属于同一产品族) 一起使用创建对象需要大量的重复代码
- 提供一个产品类的库,所有的产品以同样的接口出现,从而使得客户端不依赖具体的实现
-
优点:
- 具体产品在应用层的代码隔离,无需关心创建的细节
- 将一个系列的产品统一到一起创建
-
缺点:
- 规定了所有可能被创建的产品集合,产品簇中扩展新的产品困难;
- 增加了系统的抽象性和理难度
小结:
- 简单工厂模式(静态工厂模式)
- 虽然某种程度上不符合设计原则,但实际使用最多
- 工厂方法模式
- 不修改已有类的前提下,通过增加新的工厂类实现扩展
- 抽象工厂模式
- 不可以增加产品,可以增加产品族
应用场景:
- JDK中Calendar的getInstance方法
- JDBC中的Connection对象的获取
- Spring 中的IOC 容器创建管理bean对象
- 反射中Class对象的newInstance方法
4.构造者模式
定义:将一个复杂对象的构建与表示分离
作用:在用户不知道对象的建造过程和细节的情况下可以直接创建复杂对象
角色
- Builder
- 抽象建造者: 为创建一个产品指定构造部件的方法,由具体建造者去实现
- 具体实现来依赖Builder接口 面向接口编程
- Worker
- 具体建造者: 实现抽象建造者的方法,构造或装配对象
- Builder的实现类,创建复杂对象的细节在实现方法中
- Product
- 产品: 表示被建造的复杂对象,内部由ConcreteBuild去实现,最终由Director生产出来
- 最终被创建出来的产品
- Director
- 指挥者: 调用Worker实现的建造方法,生产出Product产品
- 接收Builder的实现类Worker,调用创建对象详细方法,返回产品
代码:
1.Builder(抽象建造者)
package com.wdzl.builder;
//抽象建造者
public abstract class Builder {
abstract Builder buildA(String msg);
abstract Builder buildB(String msg);
abstract Builder buildC(String msg);
abstract Builder buildD(String msg);
abstract Product product();
}
2.Product
package com.wdzl.builder;
//产品,默认套餐
public class Product {
private String buildA="可乐";
private String buildB="炸鸡";
private String buildC="汉堡";
private String buildD="薯条";
@Override
public String toString() {
return "Product{" +
"buildA='" + buildA + '\'' +
", buildB='" + buildB + '\'' +
", buildC='" + buildC + '\'' +
", buildD='" + buildD + '\'' +
'}';
}
public void setBuildA(String buildA) {
this.buildA = buildA;
}
public void setBuildB(String buildB) {
this.buildB = buildB;
}
public void setBuildC(String buildC) {
this.buildC = buildC;
}
public void setBuildD(String buildD) {
this.buildD = buildD;
}
}
3.Worker(具体建造者)
package com.wdzl.builder;//具体建造者public class Worker extends Builder { private Product product; public Worker(){ product=new Product(); } @Override Builder buildA(String msg) { product.setBuildA(msg); return this; } @Override Builder buildB(String msg) { product.setBuildB(msg); return this; } @Override Builder buildC(String msg) { product.setBuildC(msg); return this; } @Override Builder buildD(String msg) { product.setBuildD(msg); return this; } @Override Product product() { return product; }}
4.Dirctor(客户指定生产)
package com.wdzl.builder;
//客户
public class Client {
public static void main(String[] args) {
//服务员
Worker worker = new Worker();
Product product = worker.buildA("圣代").buildB("全家桶").buildC("雪碧").buildD("鸡腿").product();
System.out.println(product);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fK9IraKJ-1629277836983)(
)]
- 优点
- 产品的创建和表示分离,客户端不必知道产品内部组成细节(房子内部组成细节是工人去做的,我不用知道)如果想要改变产品内部,只需要定义一个新的具体建造者
- 缺点
- 建造者模式建造的产品一般具有较多相同点,如果差异大,不适合使用建造者模式
- 应用场景
- 产品对象内部很复杂,产品对象有很多共同点
5.原型模式
Prototypye
简介
原型模式
类似复制粘贴功能
创建一个实例,用这个实例去拷贝创建新实例(直接从内存中拷贝)
浅拷贝
基本类型拷贝是值传递,引用类型拷贝的是同一个内存地址
package com.wdzl.cloneable;
import java.util.Date;
public class File implements Cloneable{
private String name;
private Date createTime;
@Override
public String toString() {
return "File{" +
"name='" + name + '\'' +
", createTime=" + createTime +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public File(String name, Date createTime) {
this.name = name;
this.createTime = createTime;
}
public File() {
}
/**
* 重写clone方法
* @return
* @throws CloneNotSupportedException
*/
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
测试:
package com.wdzl.cloneable;import java.util.Date;public class Client { public static void main(String[] args) throws CloneNotSupportedException { Date date=new Date(); File file=new File("文件1",date); File cloneFile = (File) file.clone(); System.out.println(file==cloneFile); System.out.println(file.hashCode()); System.out.println(cloneFile.hashCode()); System.out.println(file); System.out.println(cloneFile); System.out.println("------------------------"); date.setTime(22222222); System.out.println(file); System.out.println(cloneFile); }}
浅拷贝,当我们Date对象改变时,两个file对象里的data也会发生相应的变化,因为这两个对象都指向同一个date对象
深拷贝
基本类型拷贝是值传递,引用类型拷贝是开辟另一块内存地址,将原数据拷贝到新内存地址上
/** * 重写clone方法 * @return * @throws CloneNotSupportedException */ @Override protected Object clone() throws CloneNotSupportedException { Object clone = super.clone(); ((File)clone).createTime = (Date) this.getCreateTime().clone(); return clone; }
6.适配器模式
作用:将一个接口转换成客户希望的另一个一个接口
适配器模式使得原来接口不兼容而不能一起工作的类能够一起工作
类适配器模式(继承)
1.被适配的类
package com.wdzl.adapter;//被适配的类 网线public class adaptee { public void request(){ System.out.println("上网。。打游戏、、"); }}
2.客户端类
//客户端类:电脑想上网插网线public class Computer { public void net(NetToUsb adapter) { adapter.handleRequest(); }}
3.接口转换器的抽象实现
//接口转换器的抽象实现public interface NetToUsb { public void handleRequest();}
4.真正的适配器,接口转换器的具体实现
package com.wdzl.adapter;
//转接器,真正的适配器
public class Adapter extends adaptee implements NetToUsb{
// private adaptee adaptee;
// public Adapter(adaptee adaptee){
// this.adaptee=adaptee;
// }
@Override
public void handleRequest() {
super.request();
}
}
5.测试
public class Test {
public static void main(String[] args) {
Computer computer = new Computer();
// adaptee adaptee = new adaptee();
Adapter adapter = new Adapter();
computer.net(adapter);
}
}
对象适配器模式(组合)
适配器
package com.wdzl.adapter;
//转接器,真正的适配器
public class Adapter implements NetToUsb{
private adaptee adaptee;
public Adapter(adaptee adaptee){
this.adaptee=adaptee;
}
@Override
public void handleRequest() {
adaptee.request();
}
}
测试
public class Test {
public static void main(String[] args) {
Computer computer = new Computer();
adaptee adaptee = new adaptee();
Adapter adapter = new Adapter(adaptee);
computer.net(adapter);
}
}
适配器模式让原本不兼容的接口能够一起工作
7.桥接模式
桥接模式:
将抽象部分与它的实现部分分离,使它们都可以独立地变化。(即常用于有两个变化维度的系统)
特点:
- 用抽象关联取代了传统的多层继承
- 将类之间的静态继承关系转换为动态的对象组合关系
1.实现类接口
//品牌
public interface Brand {
void info();
}
2.具体实现类
//华为品牌
public class HuaWei implements Brand {
@Override
public void info() {
System.out.print("华为");
}
}
//小米品牌
public class XiaoMi implements Brand{
@Override
public void info() {
System.out.print("小米");
}
}
3.抽象类
1.为什么这里是抽象类?
这里不要定义为接口,因为电脑的品牌是属性,使用组合来实现,电脑出厂是自带品牌的
因为可以和别人建立关联,如果使用接口则不能实现!
2.为什么使用protected?
被 protected 修饰的成员对于本包和其子类可见。(在子类当中使用!!)
public abstract class Phone { //组合,品牌。。 protected Brand brand; public Phone(Brand brand) { this.brand = brand; } public void info(){ //自带品牌 brand.info(); }}
4.扩充抽象类
class Ten extends Phone{
public Ten(Brand brand) {
super(brand);
}
@Override
public void info() {
super.info();
System.out.println("10系列");
}
}
class S extends Phone{
public S(Brand brand) {
super(brand);
}
@Override
public void info() {
super.info();
System.out.println("S系列");
}
}
优势
- 桥接模式偶尔类似于多继承方案,但是多继承方案违背了类的单一职责原则,复用性比较差,类的个数也非常多,桥接模式是比多继承方案更好的解决方法。极大的减少了子类的个数,从而降低管理和维护的成本
- 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。符合开闭原则,就像一座桥,可以把两个变化的维度连接起来!
劣势
- 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
- 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。
最佳实际
- 如果一个系统需要在构建的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。抽象化角色和实现化角色可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。
- 一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
- 虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。
场景
- Java语言通过Java虚拟机实现了平台的无关性。
- AWT中的Peer架构
- JDBC驱动程序也是桥接模式的应用之一。
8.代理模式
8.1静态代理模式
1.真实对象和代理对象要实现同一接口
2.代理对象要代理真实角色
public class StaticProxy {
public static void main(String[] args) {
YOU you = new YOU();
Proxy proxy=new Proxy(you);
proxy.HappyMarry();
}
interface Marry {
void HappyMarry();
}
static class YOU implements Marry{
@Override
public void HappyMarry() {
System.out.println("赵童要结婚了。。。好开心");
}
}
static class Proxy implements Marry{
private Marry WHO;
public Proxy(Marry WHO) {
this.WHO = WHO;
}
@Override
public void HappyMarry() {
before();
this.WHO.HappyMarry();
after();
}
void after(){
System.out.println("洞房花烛夜");
}
void before(){
System.out.println("久旱逢甘露");
}
}
}
8.2动态代理模式
Proxy:创建动态代理类和实例的方法
InvocationHandler:调用处理程序,并返回一个结果
Proxy.newProxyInstance(
this.getClass().getClassLoader(),
tagert.getClass().getInterfaces(),
this);
- 类加载器:真实对象.getClass().getClassLoader()`
- 接口数组:真实对象.getClass().getInterfaces()`
- 处理器:new InvocationHandler(),是个匿名对象,需实现invoke接口`
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
- proxy:代理对象`
- method:代理对象调用的方法,被封装为的对象(就把下面调用方法的String computer = proxy_lenovo.sale(8000);转到这里封装成对象的)`
- args:代理对象调用的方法时,传递的实际参数`
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
//真实对象
Host host = new Host();
//代理对象:现在没有,在运行时产生
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
proxyInvocationHandler.setTagert(host);
Rent proxy = (Rent) proxyInvocationHandler.getProxy();
proxy.rent();
}
}
//抽象类接口
interface Rent{
void rent();
}
//真实对象
class Host implements Rent{
@Override
public void rent() {
System.out.println("我要租房子。。。。。。");
}
}
//代理类
class ProxyInvocationHandler implements InvocationHandler{
//被代理的接口
private Object tagert;
public void setTagert(Object tagert) {
this.tagert = tagert;
}
//生成代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),tagert.getClass().getInterfaces(),this);
}
@Override
//实现对象的方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态本质,调用反射机制
Object invoke = method.invoke(tagert, args);
return invoke;
}
}
9.享元模式(Flyweight)
运用共享技术有效地支持大量细粒度的对象
优点: 如果系统有大量类似的对象,可以节省大量的内存及CPU资源
package com.wdzl.flyweight;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 享元模式
*/
public class FlyWeightTest {
public static void main(String[] args) {
TreeNode treeNode=new TreeNode(1,2,TreeFactory.getTree("梨树","雪梨"));
TreeNode treeNode2=new TreeNode(6,2,TreeFactory.getTree("梨树","黄梨"));
TreeNode treeNode3=new TreeNode(1,2,TreeFactory.getTree("桃树","油桃"));
TreeNode treeNode4=new TreeNode(1,2,TreeFactory.getTree("桃树","毛桃"));
}
}
/**
* 树的信息
*/
class Tree{
private final String name;
private final String data;
public Tree(String name,String data) {
System.out.println("name"+name+"-----------"+"data"+data);
this.name = name;
this.data = data;
}
public String getName() {
return name;
}
public String getData() {
return data;
}
}
/**
* 树的节点
*/
class TreeNode{
private int x;
private int y;
private Tree tree;
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public Tree getTree() {
return tree;
}
public void setTree(Tree tree) {
this.tree = tree;
}
public TreeNode(int x, int y, Tree tree) {
this.x = x;
this.y = y;
this.tree = tree;
}
}
/**
* 造树的工厂
*/
class TreeFactory{
private static Map<String,Tree> map=new ConcurrentHashMap<>();
public static Tree getTree(String name,String data){
if ( map.containsKey(name)) {
return map.get(name);
}
Tree tree = new Tree(name,data);
map.put(name,tree);
return tree;
}
}
总结
使用场景
池化技术: 缓冲池,连接池等
池里有很多事先创建好的对象,有我们需要的就可以直接拿来用,没有需要的就创建
优点
- 减少创建对象,避免占用大量内存
缺点
- 提高系统复杂度,分离出了内部状态和外部状态
- 使用时需要分离内部,外部状态,并用工厂类创建,管理元对象
10.装饰模式
- 装饰器设计模式(Decorator Pattern)
- 也叫包装设计模式,属于结构型模式,它是作为现有的 类的⼀个包装,允许向⼀个现有的对象添加新的功能, 同时⼜不改变其结构
- 给对象增加功能,⼀般两种⽅式 继承或关联组合,将⼀ 个类的对象嵌⼊另⼀个对象中,由另⼀个对象来决定是 否调⽤嵌⼊对象的⾏为来增强功能,这个就是装饰器模 式,⽐继承模式更加灵活
- 应⽤场景
- 以动态、透明的⽅式给单个对象添加职责,但⼜能不改 变其结构
- JDK源码⾥⾯应⽤的最多的就是IO流,⼤量使⽤装饰设 计模式
package com.wdzl.decorator;
/**
* 装配器模式
*/
public class DecoratorTest {
public static void main(String[] args) {
ConcreteComponent concreteComponent=new ConcreteComponent();
Decorator decorator=new SonDecorator2(new SonDecorator(concreteComponent));
decorator.operation();
}
}
/**
* 拍照方法
*/
interface Component{
void operation();
}
/**
* 拍照的实现
*/
class ConcreteComponent implements Component{
@Override
public void operation() {
System.out.println("拍照");
}
}
/**
* 注入拍照的功能
*/
abstract class Decorator implements Component{
protected Component component;
public Decorator(Component component) {
this.component = component;
}
}
class SonDecorator extends Decorator{
public SonDecorator(Component component) {
super(component);
}
@Override
public void operation() {
component.operation();
System.out.println("美颜");
}
}
class SonDecorator2 extends Decorator{
public SonDecorator2(Component component) {
super(component);
}
@Override
public void operation() {
component.operation();
System.out.println("滤镜");
}
}
- 装饰器设计模式优点
- 装饰模式与继承关系的⽬的都是要扩展对象的功能,但 装饰模式可以提供⽐继承更多的灵活性。
- 使⽤不同的具体装饰类以及这些装饰类的排列组合,可 以创造出很多不同⾏为的组合,原有代码⽆须改变,符 合“开闭原则”
- 装饰器设计模式缺点
- 装饰模式增加了许多⼦类,如果过度使⽤会使程序变得 很复杂 (多层包装)
- 增加系统的复杂度,加⼤学习与理解的难度
11.门面模式(外观模式)
Facade
外观模式为复杂的子系统提供一个共同对外的接口
package com.wdzl.facade;
/**
* 外观模式:模拟电影院
*/
public class FacadeTest {
public static void main(String[] args) {
Facade facade = new Facade();
facade.start();
System.out.println("----------");
facade.end();
}
}
/**
* 音响系统
*/
class Sound{
public void open() {
System.out.println(" open sound ");
}
public void shutdown() {
System.out.println(" shutdown sound");
}
}
/**
* 灯光系统
*/
class Light{
public void open() {
System.out.println(" open light ");
}
public void shutdown() {
System.out.println(" shutdown light");
}
}
/**
* 投影系统
*/
class Shadow{
public void open() {
System.out.println(" open projection ");
}
public void shutdown() {
System.out.println(" shutdown projection");
}
}
/**
* 门面类
*/
class Facade{
private Sound sound;
private Light light ;
private Shadow shadow;
public Facade(){
sound=new Sound();
light=new Light();
shadow=new Shadow();
}
//开始看电影
public void start(){
sound.open();
light.shutdown();
shadow.open();
}
//结束电影
public void end(){
sound.shutdown();
light.open();
shadow.shutdown();
}
}
总结
使用场景
- 子系统类复杂,使用外观模式提供简单调用的高层接口
- 将不同层分离,层与层间建立外观模式 分层设计使用
优点
- 使用外观模式可以更好的划分层次
- 外观模式对客户端屏蔽子系统的细节,使得子系统使用简单
- 外观模式对客户端与子系统间松耦合
12.组合模式
Composite
定义: 将对象组合成树形结构来表示”部分—整体”的层次结构
组合模式使得单个对象和组合对象的使用具有一致性
1.菜单组件
/**
* 菜单组件
*/
public abstract class MenuComponent {
//菜单组件名称
protected String name;
//菜单组件等级
protected int level;
//添加子菜单
public abstract void add(MenuComponent menuComponent);
//移除子菜单
public abstract void remove(MenuComponent menuComponent);
//获取指定的子菜单
public abstract MenuComponent getChild(int index);
//获取菜单或菜单项名称
public abstract String name();
//打印菜单名称
public abstract void print();
}
2,.菜单类
/**
* 菜单类
*/
public class Menu extends MenuComponent{
//菜单可以有多个子菜单或者子菜单项
private List<MenuComponent> menuComponentList=new ArrayList<MenuComponent>();
//构造方法
public Menu(String name,int level){
this.name=name;
this.level=level;
}
@Override
public void add(MenuComponent menuComponent) {
menuComponentList.add(menuComponent);
}
@Override
public void remove(MenuComponent menuComponent) {
menuComponentList.remove(menuComponent);
}
@Override
public MenuComponent getChild(int index) {
return menuComponentList.get(index);
}
@Override
public String name() {
return this.name;
}
@Override
public void print() {
for (int i = 0; i < level; i++) {
System.out.print("-");
}
System.out.println(name);
for (MenuComponent menuComponent : menuComponentList) {
menuComponent.print();//递归
}
}
}
3.菜单项类:叶子节点
**
* 菜单项类:叶子节点
*/
public class MenuItem extends MenuComponent{
public MenuItem(String name,int level){
this.name=name;
this.level=level;
}
@Override
public void add(MenuComponent menuComponent) {
System.out.println("---");
}
@Override
public void remove(MenuComponent menuComponent) {
System.out.println("---");
}
@Override
public MenuComponent getChild(int index) {
return null;
}
@Override
public String name() {
return this.name;
}
@Override
public void print() {
for (int i=0;i<level;i++){
System.out.print("-");
}
System.out.println(name);
}
}
4.测试
public class Client {
public static void main(String[] args) {
MenuComponent menu1=new Menu("菜单管理",2);
menu1.add(new MenuItem("页面访问",3));
menu1.add(new MenuItem("展开菜单",3));
menu1.add(new MenuItem("编辑菜单",3));
menu1.add(new MenuItem("删除菜单",3));
menu1.add(new MenuItem("新增菜单",3));
MenuComponent menu2=new Menu("权限配置",2);
menu2.add(new MenuItem("页面访问",3));
menu2.add(new MenuItem("提交保存",3));
MenuComponent menu3=new Menu("角色管理",2);
menu3.add(new MenuItem("页面访问",3));
menu3.add(new MenuItem("新增角色",3));
menu3.add(new MenuItem("修改角色",3));
//创建一级菜单
MenuComponent component=new Menu("系统管理",1);
component.add(menu1);
component.add(menu2);
component.add(menu3);
component.print();
}
}
总结
特点
组合模式中定义了单个对象和组合对象这样的层次结构
单个对象:Leaf角色, 无子部件 (部门)
组合对象:composite角色 (分公司->部门) 还可以组合递归下去(总公司->分公司->部门)
一致性: 使用组合模式时不用关心到底是处理单个对象还是处理组合对象
使用场景
- 处理递归,分层的结构系统
- 需要体现 部分—整体结构
- 可以忽略单个对象和组合对象不同,统一使用组合中的对象时
13.模板方法模式
模板方法模式
Template method
模板方法模式: 在抽象类中规定多个步骤方法的模板方法架构,子类去实现时不能改变架构但是可以根据子类实际情况去重写步骤方法
比如规定制作汉堡过程:
下层面包垫底->加入蔬菜->加入酱料->加入肉馅->上层面板封顶
这5个步骤可以分为5个方法,在抽象类中规定一定顺序的调用这些步骤的方法(这个方法要final修饰不可重写,防止子类改变算法架构),而子类鸡肉(或牛肉)汉堡根据实际情况去重写加入肉馅的步骤方法
抽象类是模板,抽象类的方法是模板方法,规定了一系列的步骤
package com.wdzl.template;
/**
* 模板方法模式
*/
public class TemplateMethodTest {
public static void main(String[] args) {
AbstractClass abstractClass=new SubClass();
AbstractClass abstractClass2=new SubClass2();
AbstractClass abstractClass3=new SubClass3();
abstractClass.operation();
abstractClass2.templateMethod();
abstractClass3.templateMethod();
}
}
abstract class AbstractClass{
public void operation(){
System.out.println("准备。。。。。。。。。");
System.out.println("5..........");
System.out.println("4..........");
System.out.println("3..........");
System.out.println("2..........");
System.out.println("1..........");
templateMethod();
}
abstract protected void templateMethod();
}
class SubClass extends AbstractClass{
@Override
protected void templateMethod() {
System.out.println("启动。。。。。。。。。。");
}
}
class SubClass2 extends AbstractClass{
@Override
protected void templateMethod() {
System.out.println("加速。。。。。。。。。。");
}
}
class SubClass3 extends AbstractClass{
@Override
protected void templateMethod() {
System.out.println("冲过终点。。。。。。。。。。");
}
}
总结
使用场景
完成一系列步骤,这一系列步骤基本相同,但个别步骤实现不同时
(都是制作汉堡但是放的肉馅不同)
特点
- 将一系列细节相同的代码抽象为父类,去除子类重复代码,实现复用
- 如果不用模板方法模式,制作不同的汉堡过程会有相同的步骤(重复代码) 这种相同步骤可以称为:不变行为
- 将不变行为抽取到父类中,防止重复,实现代码复用
- 模板方法要用final修饰,防止子类修改模板方法的架构
14.观察者模式
观察者模式
Observer
定义: 观察者模式是一种一对多的依赖关系,多个观察者监听一个主题对象,当主题对象发生变化时会通知所有观察者,使得它们能够自动更新自己
package com.wdzl.observer;
import java.util.ArrayList;
import java.util.List;
public class ObserverTest {
public static void main(String[] args) {
Subject subject = new Subject();
Observer task1 = new Task1();
Observer task2 = new Task2();
subject.addObserver(task1);
subject.addObserver(task2);
subject.notifyObserver("xxxxxx");
System.out.println("----------------");
subject.remove(task1);
subject.notifyObserver("yyyyyyyyyyy");
}
}
class Subject{
//容器
private List<Observer> list=new ArrayList<>();
//添加
public void addObserver(Observer observer){
list.add(observer);
}
//删除
public void remove(Observer observer){
list.remove(observer);
}
//修改
public void notifyObserver(Object object){
for (Observer observer : list) {
observer.opertion(object);
}
}
}
interface Observer{
void opertion(Object object);
}
class Task1 implements Observer{
@Override
public void opertion(Object object) {
System.out.println("task1"+object);
}
}
class Task2 implements Observer{
@Override
public void opertion(Object object) {
System.out.println("task2"+object);
}
}
总结
观察者模式是行为型模式,定义主体与观察者的一对多关系,将主体和观察者分离进行解耦
当主体发生变化时所有依赖于它的观察者都会得到通知并更新,而主体并不用去知道依赖它的观察者
特点
- 遵循开闭原则,可扩展性好
- 对观察者与被观察者(主体)进行解耦,完全隔离
使用场景
- 多个对象间是一对多的关系,一个对象发生变化时,依赖于它的对象都得到通知并更新