1.设计模式概述
1.什么是设计模式
设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。
它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。
1995年,GoF (Gang of Four,四人组/四人帮)合作出版了《设计模式:可复用面向对象软件的基础》一书,共收录了23种设计模式,从此树立了软件设计模式领域的里程碑,人称「GoF设计模式」
2.学习设计模式的意义
设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联关系和组合关系的充分理解。
- 正确使用设计模式具有以下优点:
- 可以提高程序员的思维能力、编程能力和设计能力。
- 使程序设计更加标准化、代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期。
- 使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强。
3.23种设计模式
按照设计模式的作用可分为三种模式,创建型模式、结构型模式、行为型模式
设计模式类型 | 设计模式 |
---|---|
创建型模式 | 单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式 |
结构型模式 | 适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式 |
行为型模式 | 模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式 |
4.七大设计原则
七大原则 | 解释 |
---|---|
开闭原则 | 对扩展开放,对修改关闭 |
里氏替换原则 | 在继承关系中子类可以拓展方法,不能修改父类原有方法的功能,降低需求变更带来的风险 |
依赖倒置原则 | 要面向接口编程,不要面向实现编程。高层模块不直接依赖低层模块,二者依赖其抽象 |
单一职责原则 | 控制类的粒度大小、将对象解耦、提高其内聚性。对于类/接口/方法,负责的功能单一 |
接口隔离原则 | 不使用单一的总接口 使用多个专门职责的接口,接口间产生隔离 |
迪米特法则 | 只与你的直接朋友交谈,不跟"陌生人”说话。其中一个类需要调用另一类的某一个方法的话,可以通过第三者转发这个调用。降低类与类之间的耦合 |
合成复用原则 | 尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。 |
2.创建者模式
1.单例模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点
主要解决:一个全局使用的类频繁地创建与销毁。
关键代码:构造函数是私有的,自己创建自己的唯一实例,公有的静态的方法
优点:
- 1.在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
- 2、避免对资源的多重占用(比如写文件操作)。
缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
应用场景:WEB 中的计数器
饿汉式
一上来就把所有的东西加载进来,非常浪费空间
类加载时创建(不管使不使用都创建),天生线程安全
生命周期长
/**
* 饿汉式单例
*/
public class Hungry {
/**
* 可能会浪费空间
*/
private byte[] data1=new byte[1024*1024];
private byte[] data2=new byte[1024*1024];
private byte[] data3=new byte[1024*1024];
private byte[] data4=new byte[1024*1024];
private Hungry(){
}
private final static Hungry hungry = new Hungry();
public static Hungry getInstance(){
return hungry;
}
}
懒汉式
第一次调用才初始化,避免内存浪费。
单线程安全,多线程不安全
生命周期短
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
这里可以给他加锁完成线程安全,但是就降低了效率
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
DCL懒汉式(双重校验(判定)锁)
这里new对象的时候会有原子性不一致的问题,就导致可能出现指令重排的问题,给LazyMan2的类对象加一个volatile保证指令重排的问题
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
静态内部类
线程安全,功能和双重校验锁一样
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 void whateverMethod() {
}
}
2.工厂模式
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象
- 三种模式:
- 简单工厂模式
- 用来生产同一等级结构中的任意产品(对于增加新的产品,需要扩展已有代码)
- 工厂方法模式
- 用来生产同一等级结构中的固定产品(支持增加任意产品)
- 抽象工厂模式
- 围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。
- 简单工厂模式
- 小结:
- 简单工厂模式(静态工厂模式)
- 虽然某种程度上不符合设计原则,但实际使用最多!
- 工厂方法模式
- 不修改已有类的前提下,通过增加新的工厂类实现扩展。
- 抽象工厂模式
- 不可以增加产品,可以增加产品族!
- 简单工厂模式(静态工厂模式)
应用场景:JDBC中的Connection对象的获取、Spring中IOC容器创建管理bean对象、反射中Class对象的newInstance方法
简单工厂模式(静态工厂模式)
public interface Car {
void name();
}
class Wuling implements Car {
@Override
public void name() {
System.out.println("五菱");
}
}
class Tesla implements Car {
@Override
public void name() {
System.out.println("特斯拉");
}
}
class CarFactory {
public static Car getCar(String car) {
if ("五菱".equals(car)) {
return new Wuling();
} else if ("特斯拉".equals(car)) {
return new Tesla();
} else {
return null;
}
}
}
class Consumer {
public static void main(String[] args) {
//传统方式
//Car wuling = new Wuling();
//Car tesla = new Tesla();
//工厂模式
Car car = CarFactory.getCar("五菱");
Car car1 = CarFactory.getCar("特斯拉");
car.name();
car1.name();
}
}
这时我想要在加一辆车,那么车工厂就要增加一个大众类型的车,按照类型去寻找车
工厂方法
工厂方法,增加一层工厂的约束,对工厂去管理,要什么车就去什么工厂,然后工厂帮你找到车
public interface Car {
void name();
}
class Wuling implements Car {
@Override
public void name() {
System.out.println("五菱");
}
}
class Tesla implements Car {
@Override
public void name() {
System.out.println("特斯拉");
}
}
interface CarFactory{
Car getCar();
}
class TeslaFactory implements CarFactory{
@Override
public Car getCar() {
return new Tesla();
}
}
class WulingFactory implements CarFactory{
@Override
public Car getCar() {
return new Wuling();
}
}
class Consumer {
public static void main(String[] args) {
//传统方式
//Car wuling = new Wuling();
//Car tesla = new Tesla();
//工厂模式
//Car car = CarFactory.getCar("五菱");
//Car car1 = CarFactory.getCar("特斯拉");
//car.name();
//car1.name();
Car car = new WulingFactory().getCar();
Car car1 = new TeslaFactory().getCar();
car1.name();
car.name();
}
}
3.抽象工厂模式
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
何时使用:系统的产品有多于一个的产品族,而系统只消费其中某一族的产品
优点:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象
缺点:产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。
注意事项:产品族难扩展,产品等级易扩展。
//手机接口
public interface IphoneProduct {
void start();
void shutdown();
void callup();
void sendSMS();
}
//路由器接口
public interface IRouterProduct {
void start();
void shutdown();
void openwife();
void setting();
}
//华为手机
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 HuaweiRouter implements IRouterProduct{
@Override
public void start() {
System.out.println("开启华为路由器");
}
@Override
public void shutdown() {
System.out.println("关闭华为路由器");
}
@Override
public void openwife() {
System.out.println("开启华为wifi");
}
@Override
public void setting() {
System.out.println("打开华为设置");
}
}
//小米手机
public class XiaomiPhone implements IphoneProduct{
@Override
public void shutdown() {
System.out.println("关闭小米手机");
}
@Override
public void callup() {
System.out.println("小米手机打电话");
}
@Override
public void sendSMS() {
System.out.println("小米手机发短信");
}
@Override
public void start() {
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 openwife() {
System.out.println("开启小米wifi");
}
@Override
public void setting() {
System.out.println("开启小米设置");
}
}
//生产路由器和手机的工厂
public interface IProductFactory {
//生产手机
IphoneProduct iphoneProduct();
//生产路由器
IRouterProduct irouterproduct();
}
//生产华为系列路由器和手机的工厂
public class HuaweiFactory implements IProductFactory{
@Override
public IphoneProduct iphoneProduct() {
return new HuaweiPhone();
}
@Override
public IRouterProduct irouterproduct() {
return new HuaweiRouter();
}
}
//生产小米系列路由器和手机的工厂
public class XiaomiFactory implements IProductFactory{
@Override
public IphoneProduct iphoneProduct() {
return new XiaomiPhone();
}
@Override
public IRouterProduct irouterproduct() {
return new XiaomiRouter();
}
}
//主机,去找一个商品直接去该系列的工厂去寻找并使用
public class Clinet {
public static void main(String[] args) {
System.out.println("==========小米系列产品==========");
//小米工厂
XiaomiFactory xiaomiFactory = new XiaomiFactory();
IphoneProduct iphoneProduct = xiaomiFactory.iphoneProduct();
iphoneProduct.start();
iphoneProduct.shutdown();
System.out.println("==========华为系列产品==========");
//小米工厂
HuaweiFactory huaweiFactory = new HuaweiFactory();
iphoneProduct = huaweiFactory.iphoneProduct();
iphoneProduct.start();
iphoneProduct.shutdown();
}
}
4.建造者模式
将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
建造者:创建和提供实例,导演:管理建造出来的实例的依赖关系。
与工厂模式的区别是:建造者模式更加关注与零件装配的顺序
优点: 1、建造者独立,易扩展。 2、便于控制细节风险。
缺点: 1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类。
使用场景: 1、需要生成的对象具有复杂的内部结构。 2、需要生成的对象内部属性本身相互依赖。
- 建造者与抽象工厂模式的比较:
- 与抽象工厂 模式相比,建造者模式返回一个组装好的完整产品,而抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族。
- 在抽象工厂模式中,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者类来指导如何生成对象,包括对象的组装过程和建造步骤,它侧重于一步步构造一 个复杂对象, 返回一个完整的对象。
- 如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车!
public abstract class Builder {
abstract void builderA();//地基
abstract void builderB();//钢筋水泥
abstract void builderC();//铺电线
abstract void builderD();//粉刷
//完工:得到产品
abstract Product getproduct();
}
//产品:房子
public class Product {
private String builderA;
private String builderB;
private String builderC;
private String builderD;
//get set tostring
}
//具体建造者:工人
public class Worker extends Builder{
private Product product;
public Worker() {
product = new Product();
}
@Override
void builderA() {
product.setBuilderA("地基");
System.out.println("地基");
}
@Override
void builderB() {
product.setBuilderB("钢筋水泥");
System.out.println("钢筋水泥");
}
@Override
void builderC() {
product.setBuilderC("铺电线");
System.out.println("铺电线");
}
@Override
void builderD() {
product.setBuilderD("粉刷");
System.out.println("粉刷");
}
@Override
Product getproduct() {
return product;
}
}
//指挥:核心。负责构建一个工程,工程如何构建,由他决定。
public class Director {
//指挥工人按照顺序建房子
public Product build(Builder builder){
builder.builderA();
builder.builderB();
builder.builderD();
builder.builderC();
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);
}
}