设计模式
1. OOP七大原则
- 开闭原则: 对扩展开放,对修改关闭
- 里氏替换原则:继承必须确保超类所拥有的性质在子类中仍然成立
- 依赖倒置原则:面向接口编程,不面向实现编程
- 单一职责原则:控制类的粒度大小,将对象解耦、提高其内聚性
- 接口隔离原则:要为各个类建立需要的专用接口
- 迪米特法则: 只与直接朋友交谈,不跟陌生人说话
- 合成复用原则:尽量先使用组合或者聚合等关联关系来实现,其次才考虑继承关系
2. 单例模式
饿汉式单例
// 饿汉式单例
public class Hungry {
// 类一加载就分配好内存了,可能会造成空间的浪费
private byte[] data1 = new byte[1024];
private byte[] data2 = new byte[1024];
private byte[] data3 = new byte[1024];
private byte[] data4 = new byte[1024];
// 构造器私有,保证只有一个对象
private Hungry(){
}
private final static Hungry HUNGRY = new Hungry();// 直接将对象创建好
public static Hungry getInstance(){
return HUNGRY;
}
}
DLC懒汉式单例
// 懒汉式单例
public class LazyMan {
private LazyMan(){
}
private static LazyMan lazyMan;
public static LazyMan getInstance(){
if(lazyMan == null){
lazyMan = new LazyMan();
}
return lazyMan;
}
// 单线程下正常,可在多线程下就有可能打乱操作 因此需要双重检测锁模式
package com.hua.single;
import java.lang.reflect.Constructor;
// 懒汉式单例
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();// 不是一个原子性操作
/**
* 1. 分配内存空间
* 2. 执行构造方法,初始化对象
* 3. 把这个对象指向这个空间
*
* 真实情况可能会将这个顺序打乱执行,需要加上volatile
* */
}
}
}
return lazyMan;
}
此时使用反射机制可以破坏单例模式
package com.hua.single;
import java.lang.reflect.Constructor;
// 懒汉式单例
public class LazyMan {
private static boolean flag = false;
private LazyMan(){
synchronized (LazyMan.class){
if(flag == false){
flag = true;
}else {
throw new RuntimeException("不要试图使用反射机制创建对象");
}
}
}
private volatile static LazyMan lazyMan;
// 双重检测锁模式的懒汉式单例 DCL懒汉式
public static LazyMan getInstance(){
if(lazyMan == null){
synchronized (LazyMan.class){
if(lazyMan == null){
lazyMan = new LazyMan();// 不是一个原子性操作
/**
* 1. 分配内存空间
* 2. 执行构造方法,初始化对象
* 3. 把这个对象指向这个空间
*
* 真实情况可能会将这个顺序打乱执行,需要加上volatile
* */
}
}
}
return lazyMan;
}
public static void main(String[] args) throws Exception{
LazyMan instance1 = LazyMan.getInstance();
Constructor<LazyMan> constructor = LazyMan.class.getDeclaredConstructor(null);
constructor.setAccessible(true);
LazyMan instance2 = constructor.newInstance();
LazyMan instance3 = constructor.newInstance();
System.out.println(instance1);
System.out.println(instance3); // com.hua.single.LazyMan@1b6d3586
System.out.println(instance2); // com.hua.single.LazyMan@4554617c
}
}
/*
另外使用反射机制,就可以破环单例模式,使用反射获取私有构造器,使用构造构造器再创建一个lazyMan对象,这样就可以得到两个不一样的对象,但是这种方式也可以避免,就是在私有构造器中加锁
构造器加锁之后,如果使用反射多次创建对象,这样创建的对象同样是不同的,这样就可以添加一个标志位
这样之后如果通过某种手段获得该标志位的字段名,这样在两次创建对象之间将该字段修改为初始状态,这样就可破坏单例模式,这就需要使用枚举类型
*/
静态内部类单例
// 静态内部类
public class Holder {
private Holder(){
}
private Holder getInstance(){
return InnerClass.HOLDER;
}
public static class InnerClass{
private static final Holder HOLDER = new Holder();
}
}
单例不安全,因为有反射,使用枚举
package com.hua.single;
import java.lang.reflect.Constructor;
// enum本身是一个类
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) throws Exception {
EnumSingle instance1 = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
EnumSingle instance2 = declaredConstructor.newInstance();
// Exception in thread "main" java.lang.NoSuchMethodException: com.hua.single.EnumSingle.<init>()
// 报错说没有无参构造的方法
System.out.println(instance1);
System.out.println(instance2);
}
}
使用jad将.class进行编译为.java jad -sjava EnumSingle.class
可以看到此处为有参构造,参数类型分别为String 和 int
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
// IllegalArgumentException: Cannot reflectively create enum objects
3. 工厂模式
实现了创建者和调用者分离
开闭原则,依赖倒转原则,迪米特法则
本质:
- 实例化对象不适用new,用工厂方法代替
- 将选择实现类,创建对象统一管理和控制,从而将调用者和实现类分离
三种模式
- 简单工厂模式
用来生产统一等级结构中的任意产品 如果想要增加新的产品,需要扩展已有代码
// 接口
public interface Car {
void name();
}
// 实现类
public class Tesla implements Car{
@Override
public void name() {
System.out.println("特斯拉");
}
}
// 实现类
public class Wuling 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 static Car getTesla(){
return new Tesla();
}
public static Car getWuling(){
return new Wuling();
}
}
// 调用者
public class Consumer {
public static void main(String[] args) {
Car tesla = CarFactory.getCar("特斯拉");
tesla.name();
}
}
// 该模式不能实现开闭原则,每次增加新的类都需要修改工厂
- 工厂方法模式
用来生产同一等级结构中的固定产品 需要添加工厂类
// 车接口
public interface Car {
void name();
}
// 实现类
public class Tesla implements Car{
@Override
public void name() {
System.out.println("特斯拉");
}
}
// 实现类
public class Wuling implements Car{
@Override
public void name() {
System.out.println("五菱宏光");
}
}
// 工厂接口
public interface CarFactory {
Car getCar();
}
// 实现类
public class TeslaFactory implements CarFactory{
@Override
public Car getCar() {
return new Tesla();
}
}
// 实现类
public class WulingFactory implements CarFactory{
@Override
public Car getCar() {
return new Wuling();
}
}
// 调用者
public class Consumer {
public static void main(String[] args) {
Car tesla = new TeslaFactory().getCar();
Car wuling = new WulingFactory().getCar();
tesla.name();
wuling.name();
}
}
// 这样当增加一个类的时候,只需添加该类,实现车接口,添加该类的工厂,实现工厂接口,调用该车的工厂即可,不会对原来的工厂进行修改。
根据设计原则:工厂方法模式
根据实际业务:简单工厂模式
- 抽象工厂模式
围绕一个超级工厂创建其他工厂
4. 抽象工厂模式
定义:抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定具体的类
不能增加产品,但是能增加产品族 产品族类似于品牌,华为,小米
使用场景:
- 客户端不依赖于产品类实例如何被创建、实现等细节
- 强调一系列相关的产品对象一起使用创建对象需要大量的重复代码
- 提供一个产品类的库,所有产品以同样的接口出现,从而使客户端不依赖于具体的实现
优点:
- 具体产品在应用层的代码隔离,无需关系创建的细节
- 将一个系列的产品统一到一起创建
缺点:
- 规定了所有可能被创建的产品的集合,产品簇中扩展新的产品困难
- 增加了系统的抽象性和理解难度
// 产品接口
// 手机产品接口
public interface PhoneProduct {
void start();
void shutdown();
void call();
void message();
}
// 路由器产品接口
public interface RouterProduct {
void start();
void shutdown();
void setting();
void openWifi();
}
// 产品的具体实现类
// 华为手机
public class HuaweiPhone implements PhoneProduct{
@Override
public void start() {
System.out.println("华为开机");
}
@Override
public void shutdown() {
System.out.println("华为关机");
}
@Override
public void call() {
System.out.println("华为打电话");
}
@Override
public void message() {
System.out.println("华为发短信");
}
}
// 小米手机
public class XiaomiPhone implements PhoneProduct{
@Override
public void start() {
System.out.println("小米开启");
}
@Override
public void shutdown() {
System.out.println("小米关机");
}
@Override
public void call() {
System.out.println("小米打电话");
}
@Override
public void message() {
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 setting() {
System.out.println("华为路由器设置");
}
@Override
public void openWifi() {
System.out.println("华为路由器打开Wifi");
}
}
// 小米路由器
public class XiaomiRouter implements RouterProduct{
@Override
public void start() {
System.out.println("小米路由器开机");
}
@Override
public void shutdown() {
System.out.println("小米路由器关机");
}
@Override
public void setting() {
System.out.println("小米路由器设置");
}
@Override
public void openWifi() {
System.out.println("小米路由器打开Wifi");
}
}
// 产品工厂接口
// 抽象产品工厂
public interface ProductFactory {
// 生产手机
PhoneProduct phoneProduct();
// 生产路由器
RouterProduct routerProduct();
}
// 华为产品工厂
public class HuaweiFactory implements ProductFactory{
@Override
public PhoneProduct phoneProduct() {
return new HuaweiPhone();
}
@Override
public RouterProduct routerProduct() {
return new HuaweiRouter();
}
}
// 小米产品工厂
public class XiaomiFactory implements ProductFactory{
@Override
public PhoneProduct phoneProduct() {
return new XiaomiPhone();
}
@Override
public RouterProduct routerProduct() {
return new XiaomiRouter();
}
}
// 客户
public class Client {
public static void main(String[] args) {
System.out.println("-------------小米系列产品--------------");
PhoneProduct xiaomiPhone = new XiaomiFactory().phoneProduct();
RouterProduct xiaomiRouter = new XiaomiFactory().routerProduct();
xiaomiPhone.start();
xiaomiRouter.start();
System.out.println("-------------华为系列产品--------------");
PhoneProduct huaweiPhone = new HuaweiFactory().phoneProduct();
RouterProduct huaweiRouter = new HuaweiFactory().routerProduct();
huaweiPhone.start();
huaweiRouter.openWifi();
}
}
5. 建造者模式
属于创建型模式,提供了一种创建对象的最佳方式
定义:将一个复杂对象的构建与他的表示分离,使得同样的构建过程可以创建不同的表示
作用:在用户不知道对象的建造过程和细节的情况下可以直接创建复杂的对象
用户只需给出指定复杂对象的类型和内容,建造者模式负责按照顺序创建复杂对象
// 抽象的建造者 盖房子应有的步骤
public abstract class Builder {
abstract void build1();
abstract void build2();
abstract void build3();
abstract void build4();
// 经过1234步骤得到一个产品 房子
abstract Product getProduct();
}
// 产品 房子
public class Product {
private String build1;
private String build2;
private String build3;
private String build4;
public Product() {
}
public Product(String build1, String build2, String build3, String build4) {
this.build1 = build1;
this.build2 = build2;
this.build3 = build3;
this.build4 = build4;
}
public String getBuild1() {
return build1;
}
public void setBuild1(String build1) {
this.build1 = build1;
}
public String getBuild2() {
return build2;
}
public void setBuild2(String build2) {
this.build2 = build2;
}
public String getBuild3() {
return build3;
}
public void setBuild3(String build3) {
this.build3 = build3;
}
public String getBuild4() {
return build4;
}
public void setBuild4(String build4) {
this.build4 = build4;
}
@Override
public String toString() {
return "Product{" +
"build1='" + build1 + '\'' +
", build2='" + build2 + '\'' +
", build3='" + build3 + '\'' +
", build4='" + build4 + '\'' +
'}';
}
}
// 具体的建造者 工人
public class Worker extends Builder {
private Product product;
public Worker() {
this.product = new Product();
}
@Override
void build1() {
product.setBuild1("打地基");
System.out.println("打地基");
}
@Override
void build2() {
product.setBuild2("钢筋");
System.out.println("钢筋");
}
@Override
void build3() {
product.setBuild3("铺电线");
System.out.println("铺电线");
}
@Override
void build4() {
product.setBuild4("粉刷墙面");
System.out.println("粉刷墙面");
}
@Override
Product getProduct() {
return product;
}
}
// 指挥
public class Director {
// 指挥工人按照顺序建房子
public Product build(Builder builder){
builder.build1();
builder.build2();
builder.build3();
builder.build4();
return builder.getProduct();
}
}
public class Test {
public static void main(String[] args) {
// 指挥
Director director = new Director();
// 指挥具体的工人 完成产品
Product product = director.build(new Worker());
System.out.println(product.toString());
}
}
// 抽象的建造者
public abstract class Builder {
abstract Builder build1(String msg); // 汉堡
abstract Builder build2(String msg); // 可乐
abstract Builder build3(String msg); // 薯条
abstract Builder build4(String msg); // 甜点
abstract Product getProduct();
}
// 产品 肯德基
public class Product {
private String build1 = "汉堡";
private String build2 = "可乐";
private String build3 = "薯条";
private String build4 = "甜点";
public String getBuild1() {
return build1;
}
public void setBuild1(String build1) {
this.build1 = build1;
}
public String getBuild2() {
return build2;
}
public void setBuild2(String build2) {
this.build2 = build2;
}
public String getBuild3() {
return build3;
}
public void setBuild3(String build3) {
this.build3 = build3;
}
public String getBuild4() {
return build4;
}
public void setBuild4(String build4) {
this.build4 = build4;
}
@Override
public String toString() {
return "Product{" +
"build1='" + build1 + '\'' +
", build2='" + build2 + '\'' +
", build3='" + build3 + '\'' +
", build4='" + build4 + '\'' +
'}';
}
}
// 具体的建造者
public class Worker extends Builder {
private Product product;
public Worker() {
this.product = new Product();
}
@Override
Builder build1(String msg) {
product.setBuild1(msg);
return this;
}
@Override
Builder build2(String msg) {
product.setBuild2(msg);
return this;
}
@Override
Builder build3(String msg) {
product.setBuild3(msg);
return this;
}
@Override
Builder build4(String msg) {
product.setBuild4(msg);
return this;
}
@Override
Product getProduct() {
return product;
}
}
public class Test {
public static void main(String[] args) {
// 服务员
Worker worker = new Worker();
// 链式编程 在原来的基础上可以自由组合,不组合也会有默认值
Product product = worker
.build1("鸡腿")
.getProduct();
System.out.println(product.toString());
}
}
6. 原型模式
/**
* 1. 实现一个接口
* 2. 重写一个方法
* */
// Video
public class Video implements Cloneable{
private String name;
private Date creatTime;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public Video() {
}
public Video(String name, Date creatTime) {
this.name = name;
this.creatTime = creatTime;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getCreatTime() {
return creatTime;
}
public void setCreatTime(Date creatTime) {
this.creatTime = creatTime;
}
@Override
public String toString() {
return "Video{" +
"name='" + name + '\'' +
", creatTime=" + creatTime +
'}';
}
}
// 客户端 克隆
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
// 原型对象
Video v1 = new Video("哈哈哈哈哈", new Date());
System.out.println("v1:"+v1);
System.out.println("v1.hashcode:"+v1.hashCode());
// v2 克隆 v1
Video v2 = (Video)v1.clone(); //克隆的对象是与原来一样的
System.out.println("v2:"+v2);
System.out.println("v2.hashcode:"+v2.hashCode());
}
}
// 这样的克隆称为浅克隆,这里的v2是通过v1来创建的,v1指向了一个日期对象,但是此处v2也只指向了同一个日期对象,这样的话如果把v1的日期进行修改之后,v2的日期也会修改。
// 实现深克隆
/**
* 1. 实现一个接口
* 2. 重写一个方法
* */
// Video
public class Video implements Cloneable{
private String name;
private Date creatTime;
@Override
public Object clone() throws CloneNotSupportedException {
Video obj = (Video)super.clone();
// 将对象的属性也进行克隆
obj.creatTime = (Date) this.creatTime.clone();
return obj;
}
public Video() {
}
public Video(String name, Date creatTime) {
this.name = name;
this.creatTime = creatTime;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getCreatTime() {
return creatTime;
}
public void setCreatTime(Date creatTime) {
this.creatTime = creatTime;
}
@Override
public String toString() {
return "Video{" +
"name='" + name + '\'' +
", creatTime=" + creatTime +
'}';
}
}
// Video{name='哈哈哈哈哈', creatTime=Mon Jul 11 19:20:00 CST 2022}
// Video{name='哈哈哈哈哈', creatTime=Mon Jul 11 19:20:00 CST 2022}
// Video{name='哈哈哈哈哈', creatTime=Sun Jan 04 08:57:06 CST 1970}
// Video{name='哈哈哈哈哈', creatTime=Mon Jul 11 19:20:00 CST 2022}
7. 适配器模式
模拟网线使用转换器接上电脑上网
1.继承实现 类适配器
网线
// 要适配的类
public class Adaptee {
public void request(){
System.out.println("连接网线上网");
}
}
适配器
// 接口转换器的抽象实现
public interface NetToUsb {
// 作用:处理请求
public void handleRequest();
}
// 真正的适配器
public class Adapter extends Adaptee implements NetToUsb{
@Override
public void handleRequest() {
super.request(); // 可以上网了
}
}
电脑
// 客户端类
public class Computer {
public void net(NetToUsb adapter){
adapter.handleRequest();
}
public static void main(String[] args) {
Computer computer = new Computer(); // 电脑
Adaptee adaptee = new Adaptee(); // 网线
Adapter adapter = new Adapter(); // 转接器
Adapter2 adapter2 = new Adapter2(adaptee);
computer.net(adapter);
}
}
2.组合方式 对象适配器
// 组合 对象适配器:常用
public class Adapter2 implements NetToUsb{
private Adaptee adaptee;
public Adapter2(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void handleRequest() {
adaptee.request();
}
}
对象适配器的优点
- 一个对象适配器可以把多个不同的是陪着适配到同一个目标
- 可以适配一个适配者的子类,由于适配者和适配者之间是关联关系,根据里氏代换原则,适配者的子类也可通过该适配器进行适配
类适配器缺点
- 对于java、C#等不支持多种类继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适配者
- 在java、C#语言中,类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性
使用场景:
- 系统需要使用一些现有的类,而这些类的接口不符合系统的需要,甚至没有这些类的源代码
- 想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作
8. 桥接模式
桥接模式是将抽象部分与他的实现部分分离,是他们都可以独立变化,他是一种对象结构型模式,又称为柄体模式或者接口模式
// 品牌
public interface Brand {
void info();
}
// apple品牌
public class Apple implements Brand{
@Override
public void info() {
System.out.println("Apple");
}
}
// 联想品牌
public class Lenovo implements Brand{
@Override
public void info() {
System.out.println("联想");
}
}
// 抽象的电脑类型类
public abstract class Computer {
// 组合 品牌 桥
protected Brand brand;
public Computer(Brand brand) {
this.brand = brand;
}
public void info(){ // 输出品牌信息
brand.info();
}
}
class Desktop extends Computer{
public Desktop(Brand brand) {
super(brand);
}
@Override
public void info() {
super.info();
System.out.println("台式机");
}
}
class Laptop extends Computer {
public Laptop(Brand brand) {
super(brand);
}
@Override
public void info() {
super.info();
System.out.println("笔记本电脑");
}
}
public class Test {
public static void main(String[] args) {
// 苹果笔记本
Computer apple = new Laptop(new Apple());
apple.info();
// 联想台式机
Computer lenovo = new Desktop(new Lenovo());
lenovo.info();
}
}
好处分析:
- 桥接模式偶尔类似于多继承方案,但是多继承方案违背了类的单一职责原则, 复用性比较差,类的个数也非常多,桥接模式是比多继承方案更好的解决方法。极大的减少了子类的个数,从而降低管理和维护的成本
- 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。符合开闭原则,就像一座桥, 可以把两个变化的维度连接起来
坏处分析
- 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程
- 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性