设计模式
单例模式
简单点说,就是一个应用程序中,某个类的实例对象只有一个,你没有办法去new,因为构造器是被private修饰的,一般通过getInstance()方法来获取它们的实例;
1.构造方法私有化;2.实例化的变量引用私有化;3.获取实例的方法公有
getInstance()的返回值是一个对象的引用,并不是一个新的实例,所以不要错误的理解成多个对象。
单例模式的实现:
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public static Singleton getInstance(){
if(singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
这是最基本的写法,也叫懒汉式写法(线程不安全),下面再介绍几种单例模式的实现方法:
懒汉式写法(线程安全)
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
饿汉式写法
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
静态内部类
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton() {
}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
枚举
public enum Singleton {
INSTANCE;
public Singleton getInstance(){
return INSTANCE;
}
}
这种方式是《Effective Java》作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊,
单元素的枚举类型已经成为实现Singleton的最佳方法。
双重校验锁
public class Singleton {
private volatile static Singleton singleton;
private Singleton() {
}
public static Singleton getInstance() {
if(singleton == null) {
synchronized(Singleton.class) {
if(singleton == null) {
singleton == new Singleton();
}
}
}
return singleton;
}
}
观察者模式
在对象之间定义了一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象会收到通知并自动更新。
其实就是发布订阅模式,发布者发布信息,订阅者获取信息,订阅了就能收到信息,没订阅就收不到信息。
- 抽象被观察者角色:也就是一个抽象主题,它把所有对观察者对象的引用保存在一个集合中,每个主题都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者角色。一般用一个抽象类和接口来实现。
- 抽象观察者角色:为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
- 具体被观察者角色:也就是一个具体的主题,在集体主题的内部状态改变时,所有登记过的观察者发出通知。
- 具体观察者角色:实现抽象观察者角色所需要的更新接口,一边使本身的状态与制图的状态相协调。
小结:
- 这个模式是松偶合的。改变主题或观察者中的一方,另一方不会受到影像。
- JDK中也有自带的观察者模式。但是被观察者是一个类而不是接口,限制了它的复用能力。
- 在JavaBean和Swing中也可以看到观察者模式的影子。
装饰者模式
介绍:对已有的业务逻辑进一步的封装,使其增加额外的功能,Java中的IO流就是用了装饰者模式,用户在使用的时候,可以任意组装,达到自己想要的效果。
装饰者模式由四部分组成
- 抽象组件:需要装饰的抽象对象,(接口或抽象类)
- 具体组件:实现抽象组件,需要装饰的对象;
- 抽象装饰类:实现抽象组件,包含了对抽象组件的引用,并声明装饰方法;
- 具体装饰类:继承抽象装饰类,实现抽象方法,可以有多个;
示例:
/**
* 装饰器设计模式:咖啡模拟,修饰器 牛奶、糖、
* 1、抽象组件:需要装饰的抽象对象(接口或抽象父类) 饮品
* 2、具体组件:需要装饰的对象 咖啡
* 3、抽象装饰类:包含了对抽象组件的引用以及装饰着共有的方法
* 4、具体装饰类:被装饰的对象 糖、牛奶
* @author Tassel
*/
public class DecorateTest {
public static void main(String[] args) {
Drink coffee = new Coffee(); //原味咖啡
System.out.println(coffee.info() + coffee.price());
Drink milkCoffee = new Milk(coffee); // 加牛奶
System.out.println(milkCoffee.info() + milkCoffee.price());
Drink sugerCoffee = new Suger(coffee); // 加糖
System.out.println(sugerCoffee.info() + sugerCoffee.price());
}
}
// 1.抽象组件:饮品
interface Drink {
double price(); //价格
String info(); //说明
}
//2.具体组件: 咖啡
class Coffee implements Drink {
private String name = "原味咖啡";
@Override
public double price() {
return 10;
}
@Override
public String info() {
return name;
}
}
//3. 抽象装饰类
abstract class DrinkDecorate implements Drink {
//对抽象组件的引用
private Drink drink;
public DrinkDecorate (Drink drink) {
this.drink = drink;
}
@Override
public double price() {
return this.drink.price();
}
@Override
public String info() {
return this.drink.info();
}
}
//4.1 具体装饰类 : 牛奶
class Milk extends DrinkDecorate {
public Milk(Drink drink) {
super(drink);
}
@Override
public double price() {
return super.price()*4;
}
@Override
public String info() {
return super.info() + "加入了牛奶装饰";
}
}
//4.2 具体装饰类 : 糖
class Suger extends DrinkDecorate {
public Suger(Drink drink) {
super(drink);
}
@Override
public double price() {
return super.price()*2;
}
@Override
public String info() {
return super.info() + "加入了糖装饰";
}
}
适配器模式
将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
角色:
Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类;
Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类时适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系;
Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。
示例:类适配器
//适配者类
public class Adaptee {
public void adapteeRequest() {
System.out.println("被适配者的方法");
}
}
//目标接口
public interface Target {
void request();
}
public class Adapter extends Adaptee implements Target{
@Override
public void request() {
// 其他操作...
super.adapteeRequest();
// 其他操作...
}
}
示例:对象适配器
对象适配器与类适配器不同之处在于,类适配器通过继承来完成适配,对象适配器则是通过关联来完成,这里稍微修改一下 Adapter
类即可将转变为对象适配器。
public class Adapter implements Target{
// 适配者是对象适配器的一个属性
private Adaptee adaptee = new Adaptee();
@Override
public void request() {
// 其他操作...
adaptee.adapteeRequest();
// 其他操作...
}
}
静态代理模式
- RealSubject:真实对象,是实现抽象接口的类。
- Proxy:代理对象,内部含有对真实对象
RealSubject
的引用,从而可以操作真实对象。代理对象提供与真实对象相同的接口,以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。- 静态代理:有一个类文件描述代理模式
- 动态代理:在内存中形成代理类
- Subject : 接口,是对象和它的代理共用的接口,让
RealSubject
和Proxy
具有一致性。
实现步骤:
- 代理对象和真实对象实现相同的接口;
- 代理对象 = Proxy.newProxyInstance(三个参数);[动态代理]
参数:- 类加载器:真实对象.getClass().getClassLoader()
- 接口数组:真实对象.getClass().getInterfaces()
- 处理器:new InvocationHndler()
- 使用代理对象调用方法;
- 增强方法;
package com.tassel.thread;
/**
* 实现静态代理 公共接口: 1.真实角色(对象) 2.代理角色(对象)
*
*/
public class StaticProxy {
public static void main(String[] args) {
new WeddingCompany(new You()).happyMarry();
// 类似多进程的
// new Thread(线程对象).start();
}
}
// 公共接口 结婚
interface Marry {
void happyMarry();
}
// 真实角色 你结婚
class You implements Marry {
@Override
public void happyMarry() {
System.out.println("真实角色:我要结婚");
}
}
// 代理角色 婚庆公司
class WeddingCompany implements Marry {
private Marry target;
public WeddingCompany(Marry target) {
this.target = target;
}
@Override
public void happyMarry() {
System.out.println("代理角色:有人要结婚接个活");
target.happyMarry();
System.out.println("代理角色:人家结完婚了,咱该走了");
}
}
动态代理模式
- 特点:字节码随用随创建,随用随加载;
- 作用:不修改方法代码的基础上对方法增强
- 分类:
- 基于接口的动态代理
- 基于子类的动态代理
https://www.cnblogs.com/tuyang1129/p/12878549.html
基于接口的动态代理
- 涉及的类:JDK官方提供的Proxy
- 如何创建代理对象:使用Proxy类中的newProxyInstance方法
- 创建代理对象的要求:被代理类最少实现一个接口,如果没有则不能使用
- newProxyInstance方法的参数:
- ClassLoader:类加载器,用于加载代理对象字节码的,即被代理对象的类加载器
- Class[]:字节码数组,用于让代理对象和被代理对象有相同的方法。即被代理对象的接口
- InvocationHandler:它是让我们写如何代理,一般都是写一个该接口的实现类,一般情况下是匿名内部类,非必然,此接口的实现类,谁用谁写。 ```java // 被代理对象 final Producer producer = new Producer();
IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(), producer.getClass().getInterfaces(), new InvocationHandler(){ /**
* 作用:执行被代理对象的任何借口方法都会执行该方法;
* @param proxy:代理对象的引用
* @param method:当前执行的方法
* @param args:当前执行方法所需的参数
* @return:与被代理对象方法有相同的返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 提供增强的代码
Object returnValue = null;
// 获取方法执行的参数
Float money = (Float) args[0];
// 判断当前方法是不是销售
if ("saleProduct".equals(method.getName())) {
returnValue = method.invoke(producer, money * 0.8f);
}
return returnValue;
}
});
<a name="4de5d68b"></a>
### 基于子类的动态代理
- **涉及的类**:第三方cglib库,需要导jar包
- **如何创建代理对象**:使用Enhancer类中的create方法
- **创建代理对象的要求**:被代理类不能是最终类
- create方法的参数:
- Class:字节码,用于指定被代理对象的字节码
- Callback:用于提供增强的代码,它是让我们写如何代理。一般来实现该接口的子接口:MethodInterceptor
```java
Producer cglibProducer = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
/**
* 执行代理对象的任何方法都会执行该方法
* @param proxy:代理对象的引用
* @param method:当前执行的方法
* @param args:当前执行方法所需的参数
* @param
* @return
*/
@Override
public Object intercept(Object proxy, Method method, Object[] orgs, MethodProxy methodProxy) throws Throwable {
// 提供增强的代码
Object returnValue = null;
// 获取方法执行的参数
Float money = (Float) args[0];
// 判断当前方法是不是销售
if ("saleProduct".equals(method.getName())) {
returnValue = method.invoke(producer, money * 0.8f);
}
return returnValue;
}
});
工厂模式
简单工厂模式:一个抽象的接口,多个抽象接口的实现类,一个工厂类,用来实例化抽象的接口
package com.tassel.designmodel;
public class TestFactory {
public static void main(String[] args) {
Car car = Factory.getCarInstance("Benz");
if (car != null) {
car.run();
car.stop();
} else {
System.out.println("不存在此品牌车辆!");
}
}
}
abstract interface Car {
public void run();
public void stop();
}
// 具体实现类
class Benz implements Car {
public void run() {
System.out.println("Benz开始启动了。。。。。");
}
public void stop() {
System.out.println("Benz停车了。。。。。");
}
}
class Ford implements Car {
public void run() {
System.out.println("Ford开始启动了。。。");
}
public void stop() {
System.out.println("Ford停车了。。。。");
}
}
// 工厂类
class Factory {
public static Car getCarInstance(String type) {
Car car = null;
if ("Benz".equals(type)) {
car = new Benz();
}
if ("Ford".equals(type)) {
car = new Ford();
}
return car;
}
}
工厂方法模式:有四个角色,抽象工厂模式,具体工厂模式,抽象产品模式,具体产品模式。不再是由一个工厂类去实例化具体的产品,而是由抽象工厂的子类去实例化产品;
// 抽象产品角色
public interface Moveable {
void run();
}
// 具体产品角色
public class Plane implements Moveable {
@Override
public void run() {
System.out.println("plane....");
}
}
public class Broom implements Moveable {
@Override
public void run() {
System.out.println("broom.....");
}
}
// 抽象工厂
public abstract class VehicleFactory {
abstract Moveable create();
}
// 具体工厂
public class PlaneFactory extends VehicleFactory {
public Moveable create() {
return new Plane();
}
}
public class BroomFactory extends VehicleFactory {
public Moveable create() {
return new Broom();
}
}
// 测试类
public class Test {
public static void main(String[] args) {
VehicleFactory factory = new BroomFactory();
Moveable m = factory.create();
m.run();
}
}
抽象工厂模式:与工厂方法模式不同的是,工厂方法模式中的工厂只生产单一的产品,而抽象工厂模式中的工厂生产多个产品
// 抽象工厂类
public abstract class AbstractFactory {
public abstract Vehicle createVehicle();
public abstract Weapon createWeapon();
public abstract Food createFood();
}
// 具体工厂类,其中Food,Vehicle,Weapon是抽象类,
public class DefaultFactory extends AbstractFactory {
@Override
public Food createFood() {
return new Apple();
}
@Override
public Vehicle createVehicle() {
return new Car();
}
@Override
public Weapon createWeapon() {
return new AK47();
}
}
// 测试类
public class Test {
public static void main(String[] args) {
AbstractFactory f = new DefaultFactory();
Vehicle v = f.createVehicle();
v.run();
Weapon w = f.createWeapon();
w.shoot();
Food a = f.createFood();
a.printName();
}
}
简单工厂和抽象工厂的区别
简单工厂模式:
这个模式本身很简单而且使用在业务较简单的情况下。一般用于小项目或者具体产品很少扩展的情况(这样工厂类才不用经常更改)。
它由三种角色组成:
- 工厂类角色:这是本模式的核心,含有一定的商业逻辑和判断逻辑,根据逻辑不同,产生具体的工厂产品。如例子中的Driver类。
- 抽象产品角色:它一般是具体产品继承的父类或者实现的接口。由接口或者抽象类来实现。如例中的Car接口。
- 具体产品角色:工厂类所创建的对象就是此角色的实例。在java中由一个具体类实现,如例子中的Benz、Bmw类。
来用类图来清晰的表示下的它们之间的关系:
抽象工厂模式:
先来认识下什么是产品族: 位于不同产品等级结构中,功能相关联的产品组成的家族。
图中的ProductA和ProductB就是两个产品树(产品层次结构);而如图所示的ProductA1和ProductB1就是一个产品族。他们都可以放到Factory1中,因此功能有所关联。同理PorductA2和ProductB2也是一个产品族。
可以这么说,它和工厂方法模式的区别就在于需要创建对象的复杂程度上。而且抽象工厂模式是三个里面最为抽象、最具一般性的。抽象工厂模式的用意为:给客户端提供一个接口,可以创建多个产品族中的产品对象。
而且使用抽象工厂模式还要满足一下条件:
- 系统中有多个产品族,而系统一次只可能消费其中一族产品
- 同属于同一个产品族的产品以其使用。
来看看抽象工厂模式的各个角色(和工厂方法的如出一辙):
角色:
- 抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。
- 具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。在java中它由具体的类来实现。
- 抽象产品角色:它是具体产品继承的父类或者是实现的接口。在java中一般有抽象类或者接口来实现。
- 具体产品角色:具体工厂角色所创建的对象就是此角色的实例。在java中由具体的类来实现。