java 设计模式
OOP七大原则
创建型模式
用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”。GoF 中提供了单例、原型、工厂方法、抽象工厂、建造者等 5 种创建型模式。
1、单例模式
核心作用:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点
饿汉模式
// 饿汉式单例
public class Hungry{
// 可能会浪费空间
private Hungry(){}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
懒汉模式
// 懒汉式单例
public class LazyMan{
private LazyMan(){}
private volatile static LazyMan lazyMan;
// 双重检测锁模式的懒汉式单例 DCL 懒汉式
public static LazyMan getInstance(){
if(lazyMan==null){
synchronized (LazyMan.class){
if(lazyMan==null){
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
}
静态内部类单例
// 静态内部类单例
public class Holder{
private Holder(){}
public static Holder getInstace(){
return InnerClass.HOLDER;
}
public static class InnerClass{
private static final Holder HOLDER = new Holder();
}
}
枚举单例
// enum 本身也是一个Class类
public enum EnumSingle{
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
2、工厂模式
作用:实现了创建者和调用者的分离
详细分类:简单工厂模式、工厂方法模式、抽象工厂模式
OOP七大原则:
开闭原则:一个软件的实体应当对扩展开放,对修改关闭
依赖倒转原则:要针对接口编程,不要针对实现编程
地米特法则:只与你直接的朋友通信,而避免和陌生人通信
简单工厂模式
public interface Car{
void name();
}
public class WuLing implements Car{
@Override
public void name(){
System.out.println("五菱宏光");
}
}
public class Tesla implements Car{
@Override
public void name(){
System.out.println("特斯拉");
}
}
// 简单工厂,静态工厂模式
// 增加一个新的产品,如果你不修改代码,做不到
// 没有满足开闭原则
public class CarFactory{
public static Car getCar(String car){
if(car.equals("五菱")){
return new WuLing();
}else if(car.equals("特斯拉")){
return new Tesla();
}else{
return null;
}
}
}
工厂方法模式
public interface Car{
void name();
}
public class WuLing implements Car{
@Override
public void name(){
System.out.println("五菱宏光");
}
}
public class Tesla implements Car{
@Override
public void name(){
System.out.println("特斯拉");
}
}
// 工厂方法模式
public interface CarFactory{
Car getCar();
}
public class WuLingFactory implements CarFactory{
@Override
public Car getCar(){
return new WuLing();
}
}
public class TeslaFactory implements CarFactory{
@Override
public Car getCar(){
return new Tesla();
}
}
根据设计原则:工厂方法模式
根据实际业务:简单工厂模式
3、抽象工厂模式
定义:抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定它们具体的类
// 手机产品接口
public interface IphoneProduct{
void start();
void shutdown();
void callup();
void sendSMS();
}
// 路由器产品接口
public interface IRouterProduct{
void start();
void shutdown();
void openWifi();
void setting();
}
// 小米手机
public class XiaomiPhone implements IphoneProduct{
@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 HuaweiPhone implements IphoneProduct{
@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 IRouterProduct{
@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("小米设置");
}
}
// 华为路由器
public class HuaweiRouter implements IRouterProduct{
@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("华为设置");
}
}
// 抽象产品工厂
public interface IProductFactory{
// 生产手机
IphoneProduct iphoneProduct();
// 生产路由器
IRouterProduct routerProduct();
}
// 小米工厂
public class XiaomiFactory implements IProductFactory{
@Override
public IphoneProduct iphoneProduct(){
return new XiaomiPhone();
}
@Override
public IRouterProduct routerProduct(){
return new XiaomiRouter();
}
}
// 华为工厂
public class HuaweiFactory implements IProductFactory{
@Override
public IphoneProduct iphoneProduct(){
return new HuaweiPhone();
}
@Override
public IRouterProduct routerProduct(){
return new HuaweiRouter();
}
}
// 测试
public class Test{
public static void main(String[] args){
//创建小米工厂
XiaomiFactory xiaomiFactory = new XiaomiFactory();
IphoneProduct iphoneProduct = xiaomiFactory.iphoneProduct();
iphoneProduct.callup();
iphoneProduct.sendSMS();
IRouteProduct iRouterProduct = xiaomiFactory.routerProduct();
iRouterProduct.openWifi();
}
}
优点:
具体产品在应用层的代码隔离,无需关心创建细节
将一个系列的产品统一到一起创建
缺点:
规定了所有可能被创建的产品集合,产品簇中扩展最新的产品困难
增加了系统的抽象性和理解难度
4、建造者模式
建造者模式也属于创建型模式,它提供了一种创建对象的最佳方式
定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
主要作用:在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象
用户只需要给出指定复杂对象的类型和内容,建造者模式复杂按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)
// 抽象的建造者:方法
public abstract class Builder{
// 四个步骤
abstract void buildA(); // 地基
abstract void buildB(); // 钢筋工程
abstract void buildC(); // 铺电线
abstract void buildD(); // 粉刷
// 完工:得到产品
abstract Product getProduct();
}
// 产品:房子
public class Product{
private String buildA;
private String buildB;
private String buildC;
private String buildD;
public String getBuildA(){
return buildA;
}
public void setBuildA(String buildA){
this.buildA=buildA;
}
public String getBuildB(){
return buildB;
}
public void setBuildB(String buildB){
this.buildB=buildB;
}
public String getBuildC(){
return buildC;
}
public void setBuildC(String buildC){
this.buildC=buildC;
}
public String getBuildD(){
return buildD;
}
public void setBuildD(String buildD){
this.buildD=buildD;
}
@Override
public String toString(){
return "Product{buildA="+buildA+",buildB="+buildB+",buildC="+buildC+",buildD="+buildD+"}";
}
}
// 具体的建造者:工人
public class Worker extends Builder{
private Product product;
//创建产品
public Worker(){
product = new Product();
}
@Override
void buildA(){
product.setBuildA("地基");
System.out.println("地基");
}
@Override
void buildB(){
product.setBuildB("钢筋工程");
System.out.println("钢筋工程");
}
@Override
void buildC(){
product.setBuildC("铺电线");
System.out.println("铺电线");
}
@Override
void buildD(){
product.setBuildD("粉刷");
System.out.println("粉刷");
}
@Override
Product getProduct(){
return product;
}
}
// 指挥:核心。负责指挥构建一个工程,工程如何构建,由它决定
public class Director{
// 指挥工人按照顺序建造房子
public Product build(Builder builder){
builder.buildA();
builder.buildB();
builder.buildC();
builder.buildD();
return builder.getProduct();
}
}
// 测试
public class Test{
public static void main(String []args){
// 指挥
Director director = new Director();
// 指挥 具体的工人完成 产品
Product build = director.build(new Worker());
System.out.println(build.toString());
}
}
通过静态内部类方式实现零件无序装配构造,这种方式使用更加灵活,更符合定义。
// 建造者
public abstract class Builder{
abstract Builder buildA(); // 汉堡
abstract Builder buildB(); // 可乐
abstract Builder buildC(); // 薯条
abstract Builder buildD(); // 甜点
abstract Product getProduct();
}
// 产品:套餐
public class Product{
private String buildA="汉堡";
private String buildB="可乐";
private String buildC="薯条";
private String buildD="甜点";
public String getBuildA(){
return buildA;
}
public void setBuildA(String buildA){
this.buildA=buildA;
}
public String getBuildB(){
return buildB;
}
public void setBuildB(String buildB){
this.buildB=buildB;
}
public String getBuildC(){
return buildC;
}
public void setBuildC(String buildC){
this.buildC=buildC;
}
public String getBuildD(){
return buildD;
}
public void setBuildD(String buildD){
this.buildD=buildD;
}
@Override
public String toString(){
return "Product{buildA="+buildA+",buildB="+buildB+",buildC="+buildC+",buildD="+buildD+"}";
}
}
// 具体的建造者
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 getProduct(){
return product;
}
}
public class Test{
public static void main(String []args){
// 服务员
Worker worker = new Worker();
// 链式编程:在原来的基础上,可以自由组合了,如果不组合了,也有默认的套餐
Product product = worker.buildA("全家桶").buildB("雪碧").getProduct();
System.out.println(product.toString());
}
}
优点:
产品的建造和表示分离,实现了解耦。使用建造者模式可以使用客户端不必知道产品内部组成的细节。
将复杂产品的创建步骤分解在不同的方法中,使得过程更加清晰
具体的建造者类之间是相互独立的,有利于系统的扩展。增加新的具体建造者无需修改原有类库的代码,符合“开闭原则”。
缺点:
建造者模式所创造的产品一般有较多的共同点,其组成部分相似;如果产品之间的差异很大,则不合适使用建造者模式,因此其使用范围受到一定的限制。
如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变化得庞大。
5、原型模式
浅克隆
// 原型模式
/**
* 1、实现一个接口 Cloneable
* 2、重写一个方法 clone()
*/
public class Video implements Cloneable{
private String name;
private Date createTime;
@Override
protected Object clone() throws CloneNotSupportedException{
return super.clone();
}
public Video(){}
public Video(String name,Date createTime){
this.name=name;
this.createTime=createTime;
}
public void setName(String name){
this.name=name;
}
public String getName(){
return this.name;
}
public void setCreateTime(Date createTime){
this.createTime=createTime;
}
public String getCreateTime(){
return this.createTime;
}
@Override
public String toString(){
return "Video{name="+name+",createTime="+createTime+"}";
}
}
// 客户端:克隆
public class Client{
public static void main(String []args){
// 原型对象
Video v1 = new Video("test",new Date());
System.out.println("v1="+v1);
System.out.println("v1.hash="+v1.hashCode());
// v1 克隆 v2
Video v2 = (Video) v1.clone(); // 克隆出来的对象和原来是一摸一样的
System.out.println("v2="+v2);
System.out.println("v2.hashCode="+v2.hashCode());
}
}
深克隆
// 原型模式
/**
* 1、实现一个接口 Cloneable
* 2、重写一个方法 clone()
*/
public class Video implements Cloneable{
private String name;
private Date createTime;
@Override
protected Object clone() throws CloneNotSupportedException{
// 实现深克隆
Video v=(Video)super.clone();
// 将这个对象的属性也进行克隆
v.setCreateTime((Date)this.createTime.clone());
return v;
}
public Video(){}
public Video(String name,Date createTime){
this.name=name;
this.createTime=createTime;
}
public void setName(String name){
this.name=name;
}
public String getName(){
return this.name;
}
public void setCreateTime(Date createTime){
this.createTime=createTime;
}
public String getCreateTime(){
return this.createTime;
}
@Override
public String toString(){
return "Video{name="+name+",createTime="+createTime+"}";
}
}
// 客户端:克隆
public class Client{
public static void main(String []args){
// 原型对象
Video v1 = new Video("test",new Date());
System.out.println("v1="+v1);
System.out.println("v1.hash="+v1.hashCode());
// v1 克隆 v2
Video v2 = (Video) v1.clone(); // 克隆出来的对象和原来是一摸一样的
System.out.println("v2="+v2);
System.out.println("v2.hashCode="+v2.hashCode());
}
}
结构型模式
从程序的结构上实现松耦合,从而将类或对象按某种布局组成更大的结构,GoF 中提供了代理、适配器、桥接、装饰、外观、享元、组合等 7 种结构型模式。
1、适配器模式
将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本不兼容而不能一起工作的那些类可以在一起工作
1、继承:类适配器,单继承
// 要被适配的类:网线
public class Cable{
public void request(){
System.out.println("连接网线上网");
}
}
// 接口转换器的抽象类的实现
public interface NetToUsb{
// 作用:处理请求,网线 => usb
public void handleRequest();
}
// 真正的适配器,需要连接USB,连接网线
public class Adapter extends Cable implements NetToUsb{
@Override
public void handleRequest(){
super.request(); // 可以上网了
}
}
// 客户端类:想上网,插不上网线
public class Computer{
// 我们的电脑需要连接上转接器才可以上网
public void net(NetToUsb adapter){
// 上网的具体实现,找一个转接头
adapter.handleRequest();
}
}
// 测试
public class Test{
public static void main(String []args){
// 电脑,适配器,网线
Computer computer = new Computer(); // 电脑
Cable cable = new Cable(); // 网线
Adapter adapter = new Adapter(); // 转接器
computer.net(adapter);
}
}
2、组合:对象适配器,常用
// 要被适配的类:网线
public class Cable{
public void request(){
System.out.println("连接网线上网");
}
}
// 接口转换器的抽象类的实现
public interface NetToUsb{
// 作用:处理请求,网线 => usb
public void handleRequest();
}
// 真正的适配器,需要连接USB,连接网线
public class Adapter implements NetToUsb{
private Cable cable;
public Adapter(Cable cable){
this.cable=cable;
}
@Override
public void handleRequest(){
cable.request(); // 可以上网了
}
}
// 客户端类:想上网,插不上网线
public class Computer{
// 我们的电脑需要连接上转接器才可以上网
public void net(NetToUsb adapter){
// 上网的具体实现,找一个转接头
adapter.handleRequest();
}
}
// 测试
public class Test{
public static void main(String []args){
// 电脑,适配器,网线
Computer computer = new Computer(); // 电脑
Cable cable = new Cable(); // 网线
Adapter adapter = new Adapter(cable); // 转接器
computer.net(adapter);
}
}
对象适配器优点:
一个对象适配器可以把多个不同的适配者适配到同一个目标
可以适配一个适配者的子类,由于适配器和适配者之间的关系是关联关系,根据“里氏代换原则”,适配者的子类也可通过该适配器进行适配。
类适配器的缺点:
对于java、c#等不支持多重继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适配者;
在java、c#等语言中,类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性。
2、桥接模式
桥接模式是将抽象部分与它的实现部分分离,使他们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式
// 品牌
public interface Brand{
void info();
}
// 联想品牌
public class Lenovo implements Brand{
@Override
public void info(){
System.out.print("联想");
}
}
// 苹果品牌
public class Apple implements Brand{
@Override
public void info(){
System.out.print("苹果");
}
}
// 电脑的抽象类
public abstract class Computer {
// 组合品牌 桥
protected Brand brand; // protected 访问控制使得子类可以访问到父类
public Computer(Brand brand){
this.brand=brand;
}
public void info(){
brand.info(); // 自带品牌
}
}
// 台式机
public class Desktop extends Computer{
public Desktop(Brand brand){
super(brand);
}
@Override
public void info(){
super.info();
System.out.print("台式机");
}
}
// 笔记本
public class Laptop extends Computer{
public Laptop(Brand brand){
super(brand);
}
@Override
public void info(){
super.info();
System.out.print("笔记本");
}
}
public class Test {
public static void main(String []args){
// 苹果笔记本
Computer computer1 = new Laptop(new Apple());
computer1.info();
// 联想台式机
Computer computer2 = new Desktop(new Lenovo());
computer2.info();
}
}
3、代理模式
SpringAOP 的底层实现方式
代理模式分类:
静态代理
动态代理
- 1、静态代理
角色分析:
抽象角色:一般会使用接口或者抽象类来解决
真实角色:被代理的角色
代理角色:代理真实的角色,代理真实角色后,一般会做一些附属操作
客户:访问代理对象的对象
// 抽象角色 出租房子
public interface Rent{
public void rent();
}
// 真实角色 房东
public class Host implements Rent{
public void rent(){
System.out.println("房东要出租房子")
}
}
// 代理角色 中介
public class Proxy implements Rent{
private Host host;
public Proxy(){}
public Proxy(Host host){
this.host=host;
}
public void rent(){
this.seeHouse();
this.fare();
host.rent();
}
// 看房
public void seeHouse(){
System.out.println("中介带你看房");
}
// 收中介费
public void fare(){
System.out.println("收中介费");
}
}
// 测试
public class Test{
public static void main (String []args){
// 房东要租房子了
Host host = new Host();
// 代理,中介帮房东租房,但是代理一般会有一些附属操作
Proxy proxy = new Proxy();
// 你不用找房东,直接找中介即可
proxy.rent();
}
}
静态代理的好处:
可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
公共也就就交给代理角色,实现了业务的分工
公共业务发生扩展的时候,方便集中管理
- 动态代理
动态代理和静态代理角色一样
动态代理的代理类是动态生成的,不是我们直接写好的
动态代理分为两大类:基于接口的动态代理,基于类的动态代理
基于接口----JDK动态代理
基于类:cglib
java字节码实现:javasist
// 抽象角色 出租房子
public interface Rent{
public void rent();
}
// 真实角色 房东
public class Host implements Rent{
public void rent(){
System.out.println("房东要出租房子")
}
}
// 代理处理,自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler{
// 被代理的接口
private Rent rent;
public void setRent(Rent rent){
this.rent=rent;
}
// 生成得到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
}
// 处理代理实例,并返回结果
public Object invoke(Object proxy,Method method,Object [] args)throws Throwable{
// 动态代理的本质,就是使用反射机制实现的
this.seeHouse();
this.fare();
Object result = method.invoke(rent,args);
return result;
}
public void seeHouse(){
System.out.println("中介带看房子");
}
public void fare(){
System.out.println("中介收中介费");
}
}
public
// 测试
public class Test{
public static void main (String []args){
// 真实角色
Host host = new Host();
// 代理角色
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
// 通过调用程序处理角色来处理要调用接口的对象
proxyInvocationHandler.setRent(host);
Proxy proxy = (Proxy) proxyInvocationHandler.getProxy(); // 这里的proxy就是动态代理生成的
proxy.rent();
}
}
// 抽象角色
public interface UserService{
public void add();
public void delete();
public void update();
public void query();
}
// 真实角色
public class UserServiceImpl implements UserService{
public void add(){
System.out.println("增加了一个用户");
}
public void delete(){
System.out.println("删除了一个用户");
}
public void update(){
System.out.println("修改了一个用户");
}
public void query(){
System.out.println("查询了一个用户");
}
}
// 代理处理,自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler{
// 被代理的接口
private Object target;
public void setTarget(Object target){
this.target=target;
}
// 生成得到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
// 处理代理实例,并返回结果
public Object invoke(Object proxy,Method method,Object [] args)throws Throwable{
this.log(method.getName());
// 动态代理的本质,就是使用反射机制实现的
Object result = method.invoke(target,args);
return result;
}
public void log(String msg){
System.out.println("执行了:"+msg+"方法")
}
}
public
// 测试
public class Test{
public static void main (String []args){
// 真实角色
UserService userService = new UserServiceImpl();
// 代理角色
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
// 设置要代理的对象
proxyInvocationHandler.setRent(userService);
// 动态生成代理类
UserService userService = (UserService) proxyInvocationHandler.getProxy();
userService.add();
}
}
动态代理的好处:
可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
公共也就就交给代理角色,实现了业务的分工
公共业务发生扩展的时候,方便集中管理
一个动态代理类代理的是一个接口,一般就是对应的一类业务
行为型模式
用于描述类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,以及怎样分配职责。GoF 中提供了模板方法、策略、命令、职责链、状态、观察者、> > 中介者、迭代器、访问者、备忘录、解释器等 11 种行为型模式。