单例设计模式:
使用场景
分类:
实现步骤
/**
* 构造函数私有化
*/
private SingletonLazy() {
};
/**
* volatile 是java提供的关键字,可以禁止指令重排
* DCL 双重检查锁定(Double-Check-Locking) 可以在多线程情况下保持高性能
*/
private static volatile SingletonLazy instance;
public static SingletonLazy getInstance() {
// 第一重检查
if (instance == null) {
// A、B,锁定
synchronized (SingletonLazy.class) {
// 第二重检查
if (instance == null) {
instance = new SingletonLazy();
}
}
}
return instance;
}
饿汉式
private SingletonHungry(){
};
private static SingletonHungry instance = new SingletonHungry();
public static SingletonHungry getInstance(){
return instance;
}
// 1.创建抽象产品类(这里就用一个支付接口作示例)
public interface Pay {
/**
* 统一下单
*/
void unifiedOrder();
}
// 2.实现具体方法
public class ALiPay implements Pay{
/**
* 阿里统一下单
*/
public void unifiedOrder() {
System.out.println("阿里支付");
}
}
/* ******************************* */
public class WECHATPay implements Pay{
/**
* 统一下单
*/
public void unifiedOrder() {
System.out.println("微信支付");
}
}
// 3.创建工厂类,提供一个静态方法用来确定支付方式。
public class SimpleFactory {
public static Pay creatOrder(String payMethod){
if (payMethod == null){
return null;
}
if (payMethod.equalsIgnoreCase("WECHAT_PAY")){
return new WECHATPay();
}else if (payMethod.equalsIgnoreCase("ALi_PAY")){
return new ALiPay();
}
return null;
}
}
// 4.调用执行
又称工厂模式,是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则
通过工厂父类定义负责创建产品的公共接口,通过子类来确定所需要创建的类型
相比简单工厂而言,此种方法具有更多的可扩展性和复用性,同时也增强了代码的可读性
将类的实例化(具体产品的创建)延迟到工厂类的子类(具体工厂)中完成,即由子类来决定应该实例化哪一个类。
核心组成
IProduct:抽象产品类,描述所有实例所共有的公共接口
Product:具体产品类,实现抽象产品类的接口,工厂类创建对象,如果有多个需要定义多个
IFactory:抽象工厂类,描述具体工厂的公共接口
Factory:具体工场类,实现创建产品类对象,实现抽象工厂类的接口,如果有多个需要定义多个
/**
* 抽象工厂方法
*/
public interface PayFactory {
Pay getPay();
}
/**
* 具体产品工厂
*/
public class AliPayFactory implements PayFactory {
@Override
public Pay getPay() {
return new AliPay();
}
}
/**
* 抽象产品
*/
public interface Pay {
/**
* 统一下单
*/
void unifiedorder();
}
/**
* 具体产品
*/
public class AliPay implements Pay {
@Override
public void unifiedorder() {
System.out.println("支付宝支付 统一下单接口");
}
}
抽象工厂模式:基于上述两种模式的拓展,是工厂方法模式的升级版,当需要创建的产品有多个产品线时使用抽象工厂模式是比较好的选择。
1、定义两个接口 Pay、Refund
2、创建具体的Pay产品、创建具体的Refund产品
3、创建抽象工厂 OrderFactory 接口
里面两个方法 createPay/createRefund
4、创建支付宝产品族AliOderFactory,实现OrderFactory抽象工厂
5、创建微信支付产品族WechatOderFactory,实现OrderFactory抽象工厂
6、定义一个超级工厂创造器,通过传递参数获取对应的工厂
Prototype: 声明克隆方法的接口,是所有具体原型类的公共父类,Cloneable接口
ConcretePrototype : 具体原型类
Client: 让一个原型对象克隆自身从而创建一个新的对象
public class Person implements Cloneable, Serializable {
private String name;
private int age;
private List<String> info;
get and set....
@Override
protected Person clone() throws CloneNotSupportedException {
return (Person)super.clone();
}
/**
* 解决原型模式 copy出现的浅拷贝问题:只能copy基本数据类型,而对引用数据类型则指向同一个地址。因为经过序列化跟反序列化的对象内存地址会变更
* @return
*/
public Object deepClone(){
try{
// 输出 序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
// 输入 反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
Person copyObj = (Person)ois.readObject();
return copyObj;
}catch (Exception e){
e.printStackTrace();
return null;
}
}
}
public class client {
// 如果未经序列化,此时list中的数据为 aaa,ccc
public static void main(String[] args) throws CloneNotSupportedException {
Person lucy = new Person();
lucy.setName("lucy");
lucy.setAge(11);
lucy.getInfo().add("aaa");
Person jack = person1.clone();
jack.setName("jack");
person1.setAge(11);
person1.getInfo().add("ccc");
}
}
创建新对象成本较大,新的对象可以通过原型模式对已有对象进行复制来获得
如果系统要保存对象的状态,做备份使用
遗留问题:
通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的
浅拷贝实现 Cloneable,深拷贝是通过实现 Serializable 读取二进制流
拓展
如果原型对象的成员变量是基本数据类型(int、double、byte、boolean、char等),将复制一份给克隆对象;
如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址
通过覆盖Object类的clone()方法可以实现浅克隆
无论原型对象的成员变量是基本数据类型还是引用类型,都将复制一份给克隆对象,如果需要实现深克隆,可以通过序列化(Serializable)等方式来实现
原型模式是内存二进制流的拷贝,比new对象性能高很多,使用的时候记得注意是选择浅拷贝还是深拷贝
优点
缺点
/**
* 声明了建造者的公共方法
*/
public interface Builder {
/**
*细节方法
*/
void buildCpu();
void buildMainboard();
void buildDisk();
void buildPower();
void buildMemory();
Computer createComputer();
}
/**
*
* @Description 将产品和创建过程进行解耦,使用相同的创建过程创建不同的产品,控制产品生产过程
* Director是全程指导组装过程,具体的细节还是builder去操作
* @Version 1.0
**/
public class Director {
public Computer craete(Builder builder){
builder.buildMemory();
builder.buildCpu();
builder.buildMainboard();
builder.buildDisk();
builder.buildPower();
return builder.createComputer();
}
}
public class HighComputerBuilder implements Builder{
private Computer computer = new Computer();
@Override
public void buildCpu() {
computer.setCpu("高配 CPU");
}
@Override
public void buildMainboard() {
computer.setMainboard("高配 主板");
}
@Override
public void buildDisk() {
computer.setDisk("高配 磁盘");
}
@Override
public void buildPower() {
computer.setPower("高配 电源");
}
@Override
public void buildMemory() {
computer.setMemory("高配 内存");
}
@Override
public Computer createComputer() {
return computer;
}
}
建造者模式所创建的产品一般具有较多的共同点,如果产品差异大则不建议使用
JDK里面的应用
建造者模式与抽象工厂模式的比较:
见名知意,是作为两个不兼容的接口之间的桥梁,属于结构型模式
适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
常见的几类适配器
类的适配器模式
对象的适配器模式
接口的适配器模式
设计模式的疑惑
会感觉到好像是理解了模式的功能,但是一到真实的系统开发中,就不知道如何使用这个模式了
前面每个案例都有讲实际的编码操作,大家一定要充分领悟
接口适配器
有些接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要实现部分接口就可以了
// 网关接口
public interface PayGeteway {
/**
* 下单
*/
void unifiedOrder();
/**
* 退款
*/
void refund();
/**
* 查询
*/
void query();
/**
* 发红包
*/
void sendRedPack();
}
*************************
// 适配器实现
public class PayGatewayAdapter implements PayGeteway{
public void unifiedOrder() {
}
public void refund() {
}
public void query() {
}
public void sendRedPack() {
}
}
*****************************
// 继承适配器,并选择需要实现的接口
public class ProductVideoOrder extends PayGatewayAdapter{
@Override
public void unifiedOrder() {
System.out.println("统一下单");
}
@Override
public void refund() {
System.out.println("退款");
}
@Override
public void query() {
System.out.println("查询");
}
}
*****************************
public class ProductVipOrder extends PayGatewayAdapter {
@Override
public void unifiedOrder() {
System.out.println("统一下单");
}
@Override
public void refund() {
System.out.println("退款");
}
@Override
public void query() {
System.out.println("查询");
}
@Override
public void sendRedPack() {
System.out.println("发红包");
}
}
在使用一些旧系统或者是类库时,经常会出现接口不兼容的问题,适配器模式在解决这类问题具有优势
学习设计模式一定不要局限代码层面,要从软件系统整体去考虑,而不是为了使用设计模式,而去使用设计模式
想将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可
public class OldModule {
public void methodA(){
System.out.println("methodA执行了");
}
}
public interface TargetModule {
/**
* 和需要适配的类方法名(OldModule)一样
*/
void methodA();
/**
* 新的方法,如果有多个方法的话,直接编写就行
*/
void methodB();
void methodC();
}
// 继承旧模块 并实现目标模块
public class Adapter extends OldModule implements TargetModule{
public void methodB() {
System.out.println("执行了B");
}
public void methodC() {
System.out.println("执行了C");
}
}
// 测试类
TargetModule targetModule = new Adapter();
targetModule.methodA();
targetModule.methodB();
targetModule.methodC();
// 输出
methodA执行了
执行了B
执行了C