1.设计模式的目的?它出现在哪里?
目的:让程序具有更好的
- 复用性: 相同功能的代码,不用多次编写;
- 可读性: 即编程的规范性,便于其他程序员阅读;
- 可扩展性: 增加新的功能时非常的方便;
- 可靠性: 增加新功能后对原来的功能没有影响;
- 使程序呈现高内聚低耦合的特性:
出现在:面向对象 --> 功能模块{ (设计模式+算法(数据结构) ] --> 框架[使用到多种设计模式] —> 架构[服务器集群]
2.设计模式七大原则
2.1单一职责原则(Single Responsibility Principle, SRP)
对类来说,即一个类应该只负责一项职责,如类A负责两个不同的职责,职责1、职责2,当职责2需求变更而改变A时,可能造成职责2执行错误,所以要将A的粒度分解为A1,A2。
- 注意事项:
降低类的复杂度,一个类只负责一项职责
提高类的可读性,可维护性
降低变更引起的风险
通常应该遵守单一职责原则,当逻辑足够简单时,可以在代码级违反单一职责原则,在类中方法数量足够少,可以在方法级别保持单一职责原则
2.2接口隔离原则(Interface Segregation Principle,ISP)
客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上。
2.3依赖倒转原则(Dependency Inversion Principle,DIP)
- 依赖倒转原则的注意事项和细节
2.4里氏代换原则(Liskov Substitution Principle,LSP)
-
oo中的继承性的思考和说明
-
基本介绍
模拟:B继承A,并重写A中的相加方法,返回一个相减的运算结果 -
解决方法
2.5开闭原则(Open Closed Principle,OCP)
- 基本介绍
- 业务模拟
- 以上设计存在的问题
- 改进思路
2.6迪米特法则(Law of Demeter,LOD)
- 基本介绍
- 注意事项和细节
2.7合成/聚合复用原则(Composite/Aggregate Reuse Principle,CARP)
-
基本介绍
尽量使用合成聚合的方式,而不是使用继承
-
设计原则核心思想
3.UML类图(统一建模语言,描述类与类之间的关系)
- 基本介绍z
- 依赖关系
只要在类中使用到了对方,那么它们之间就存在依赖关系,如果没有对方连编译都通过不了
- 泛化(继承)关系
- 实现关系
- 关联关系
它体现的是两个类、或者类与接口之间语义级别的一种强依赖关系,普通关联关系的两个类处于同一层次上,关联可以是单向(只有一个类知道另外一个类的公共属性和操作)的也可以是双向(两个类都知道另一个类的公共属性和操作)的; - 聚合
- 组合
4.单例模式*****
单例设计模式,即某个类在整个系统中只能有一个实例对象可被获取和使用的代码模式
要点:
- 某个类只能有一个实例;
构造器私有化 - 它必须自行创建这个实例;
对外提供获取该实例对象的方式:
直接暴露;用静态变量的get方法获取 - 它必须自行向整个系统提供这个实例;
含有一个该类的静态变量来保存这个唯一的实例
4.1饿汉式:直接创建对象,不存在线程安全问题
- 直接实例化饿汉式(简洁直观)
public class Singleton1 {
public static final Singleton1 SINGLETON1 = new Singleton1();
private Singleton1() {//提供私有的构造器
}
//可以通过类型直接获取对象这里getInstance方法可以省略
public static Singleton1 getInstance() {
return SINGLETON1;
}
}
- 枚举式(最简洁)
//枚举类中的构造为私有构造
public enum Singleton2 {
INSTANCE; //编译后为public static final Singleton2 INSTANCE;
//直接使用Singleton2.INSTANCE获取对象
}
- 静态代码块饿汉式(适合复杂实例化)
public class Singleton3 {
public static final Singleton3 SINGLETON3;
private String name;
static {
Properties properties = new Properties();
try {//从配置文件中取值并在创建单例实例时set到属性中
properties.load(Singleton3.class.getClassLoader().getResourceAsStream("singletonResource.properties"));
} catch (IOException e) {
e.printStackTrace();
}
SINGLETON3 = new Singleton3((String) properties.get("name"));
}
private Singleton3(String name) {//提供私有的构造器
this.name = name;
}
public static Singleton3 getSINGLETON3() {
return SINGLETON3;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
4.2懒汉式:延迟创建对象
- 线程安全双重检查机制(适用于多线程)
public class Singleton4 {
private volatile static Singleton4 singleton=null;
private Singleton4() {}
public static Singleton4 getInstance() {
if (singleton == null) {
synchronized (Singleton4.class) {
if (singleton == null) {
singleton = new Singleton4();
}
}
}
return singleton;
}
}
- 静态内部类形式(适用于多线程)
public class Singleton5 {
private Singleton5(){
}
private Singleton5 getInstance(){
return Inner.SINGLETON_5;
}
private static class Inner{
private static final Singleton5 SINGLETON_5 = new Singleton5();
}
}
静态内部类只有在被调用时才会被加载,当用户调用的getInstance方法时才会加载内部类Singleton并且实例化Singleton实例,虚拟机会保证一个类的构造器( )方法在多线程环境中被正确地加载,同步,如果多个线程同时去初始化一个类,那么只有一个线程去执行这个类的构造器()方法,其他线程都需要阻塞等待,直到活动线程执行()方法完毕。
5.工厂模式*****
把创建对象的工作交给一个抽象出来的概念工厂去做
//计算类的基类
public abstract class Operation {
private double value1 = 0;
private double value2 = 0;
public abstract double getResult();
//getter/setter..
5.1简单(静态)工厂模式
简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。
- 组成:
Factory:这是本模式的核心,含有一定的商业逻辑和判断逻辑。在java中它往往由 一个具体类实现。(OperationFactory)
Product:它一般是具体产品继承的父类或者实现的接口。在java中由接口或者抽象类来实现。(Operation)
ConcreteProduct:工厂类所创建的对象就是此角色的实例。在java中由一个具体类实现。 来用类图来清晰的表示下的它们之间的关系(OperationAdd\OperationSub等)
//加法
public class OperationAdd extends Operation {
public double getResult() {
return getValue1() + getValue2();
}
}
//减法
public class OperationSub extends Operation {
public double getResult() {
return getValue1() - getValue2();
}
}
//乘法
public class OperationMul extends Operation {
public double getResult() {
return getValue1() * getValue2();
}
}
//工厂类
public class OperationFactory {
public static Operation createOperation(String operation) {
Operation oper = null;
switch (operation) {
case "+": oper = new OperationAdd(); break;
case "-": oper = new OperationSub(); break;
case "*": oper = new OperationMul(); break;
default:
throw new UnsupportedOperationException("不支持该操作");
}
return oper;
}}
//测试类
public static void main(String[] args) {
Operation operationAdd = OperationFactory.createOperation("+");
operationAdd.setValue1(10);
operationAdd.setValue2(5);
System.out.println(operationAdd.getResult());
}
- 特点
1.它是一个具体的类,非接口抽象类。有一个重要的createOperation()方法,利用if或者 switch创建产品并返回。
2.create()方法通常是静态的,所以也称之为静态工厂。 - 优点:
1.一个调用者想创建一个对象,只要知道其名称就可以了。
2.屏蔽产品的具体实现,调用者只关心产品的接口。 - 缺点:
1.扩展性差(增加一种算法,需要新增一个算法产品类,还要修改工厂类方法)
2.不同的产品需要不同额外参数的时候 不支持。
5.2工厂方法模式
定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。工厂方法让类的实例化推迟到子类中进行
- 组成:
Product:抽象产品(Operation)
ConcreteProduct:具体产品(OperationAdd)
Factory:抽象工厂(IFactory)
ConcreteFactory:具体工厂(AddFactory)
//工厂接口
public interface IFactory {
Operation CreateOption();
}
//加法
public class AddFactory implements IFactory {
public Operation CreateOption() {
return new OperationAdd();
}
}
//减法
public class SubFactory implements IFactory {
public Operation CreateOption() {
return new OperationSub();
}
}
//乘法
public class MulFactory implements IFactory {
public Operation CreateOption() {
return new OperationMul();
}
}
//测试类
public static void main(String[] args) {
IFactory factory = new AddFactory();
Operation operationAdd = factory.CreateOption();
operationAdd.setValue1(10);
operationAdd.setValue2(5);
System.out.println(operationAdd.getResult());
}
优点:增加新的产品类时无须修改现有系统,并封装了产品对象的创建细节,系统具有良好的灵活性和可扩展性;
缺点:增加新产品的同时需要增加新的工厂,导致系统类的个数成对增加,在一定程度上增加了系统的复杂性
5.3抽象工厂模式
提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。
- 组成:
AbstractFactory(抽象工厂):用于声明生成抽象产品的方法
ConcreteFactory(具体工厂):实现了抽象工厂声明的生成抽象产品的方法,生成一组具体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中;
AbstractProduct(抽象产品):为每种产品声明接口,在抽象产品中定义了产品的抽象业务方法;
Product(具体产品):定义具体工厂生产的具体产品对象,实现抽象产品接口中定义的业务方法。
//抽象产品:特斯拉车
public interface TeslaCar {
public void charge(); //充电
}
//抽象产品:奔驰车
public interface BencCar {
public void gasUp();//加汽油
}
//具体产品:奔驰跑车
public class BencSportCar implements BencCar {
public void gasUp() {
System.out.println("给我的奔驰跑车加最好的汽油");
}
}
//具体产品:奔驰商务车
public class BencBusinessCar implements BencCar{
public void gasUp() {
System.out.println("给我的奔驰商务车加一般的汽油");
}
}
//具体产品:特斯拉跑车
public class TeslaSportCar implements TeslaCar {
public void charge() {
System.out.println("给我特斯拉跑车冲满电");
}
}
//具体产品:特斯拉商务车
public class TeslaBusinessCar implements TeslaCar {
public void charge() {
System.out.println("不用给我特斯拉商务车冲满电");
}
}
//抽象工厂
public interface CarFactory {
public BencCar getBencCar();
public TeslaCar getTeslaCar();
}
//具体工厂:跑车工厂
public class SportCarFactory implements CarFactory {
public BencCar getBencCar() {
return new BencSportCar();
}
public TeslaCar getTeslaCar() {
return new TeslaSportCar();
}
}
//具体工厂:商务车工厂
public class BusinessCarFactory implements CarFactory {
public BencCar getBencCar() {
return new BencBusinessCar();
}
public TeslaCar getTeslaCar() {
return new TeslaBusinessCar();
}
}
//抽象工厂测试类
public static void main(String[] args) {
CarFactory carFactory = new BusinessCarFactory();
carFactory.getBencCar().gasUp();
carFactory.getTeslaCar().charge();
}
- 优点:
①隔离了具体类的生成,使得客户并不需要知道什么被创建
②每次可以通过具体工厂类创建一个产品族中的多个对象,增加或者替换产品族比较方便,增加新的具体工厂和产品族很方便; - 缺点:
增加新的产品等级结构很复杂,需要修改抽象工厂和所有的具体工厂类,对“开闭原则”的支持呈现倾斜性。
5.4三种工厂模式的对比与转换
- 简单工厂:用来生产同一等级结构中的任意产品。(对于增加新的产品,主要是新增产品,就要修改工厂类。符合单一职责原则。不符合开放-封闭原则)
- 工厂方法 :用来生产同一等级结构中的固定产品。(支持增加任意产品,新增产品时不需 要更改已有的工厂,需要增加该产品对应的工厂。符合单一职责原则、符合开放-封闭 原则。但是引入了复杂性)
- 抽象工厂 :用来生产不同产品族的全部产品。(增加新产品时,需要修改工厂,增加产品 族时,需要增加工厂。符合单一职责原则,部分符合开放-封闭原则,降低了复杂性)
5.5JDK源码分析
JDK中的calendar就是用了简单工厂模式
5.6工厂模式小结
6.代理模式*****
提供了对目标对象另外的访问方式; 即通过代理对象访问目标对象. 这样做的好处是:可以在目标对象实现的基础上, 增强额外的功能操作, 即扩展目标对象的功能.
- 静态代理
- 动态代理
7.策略模式
定义了一组算法,将每个算法都封装起来,并且使它们之间可以互换,让算法独立于使用它的客户而独立变化。
- 组成:
抽象策略角色:策略类,通常由一个接口或者抽象类实现。
具体策略角色:封装相关的算法和行为。
环境角色:持有一个策略类的引用,给客户端调用。 - 使用场景:
1.如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为
2.一个系统需要动态地在几种算法中选择一种。
//抽象策略角色
public interface Strategy
{
public int cacl(int a,int b);
}
//具体策略角色:减法
public class SubtractStraegy implements Strategy {
public int cacl(int a, int b) {
return a - b;
}
}
//具体策略角色:加法
public class AddStraegy implements Strategy {
public int cacl(int a, int b) {
return a + b;
}
}
//环境角色
public class Context {
private Strategy st;
public Context(Strategy st) {
this.st = st;
}
public int cacl(int a, int b) {
return st.cacl(a, b);
}
}
//测试类
public static void main(String[] args) {
Context context = new Context(new AddStraegy());
int cacl = context.cacl(10, 20);
System.out.println(cacl);
}
8.观察者模式*****
定义:定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖它的对象都会得到通知并自动更新。
意义:解耦!将观察者与被观察者解耦,使得他们之间的依赖性更小。
气象台发布天气情况,推送到各个平台业务:如果使用硬编码的方式各个平台使用定时任务频繁去获取天气变化必定造成资源浪费,
观察者模式提供了类似订阅发布的功能,气象台(Subject)提供登记注册、取消注册和通知功能;各个平台(Object)接收通知执行更新操作;
气象台接口
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers();
}
气象台实现
public class WeatherData implements Subject {
private float temperatrue;
private float pressure;
private float humidity;
//观察者集合
private ArrayList<Observer> observers;
public WeatherData() {
observers = new ArrayList<>();
}
//当数据有更新时,就调用 setData
public void setData(float temperature, float pressure, float humidity) {
this.temperatrue = temperature;
this.pressure = pressure;
this.humidity = humidity;
//将最新的信息 推送给 接入方 currentConditions
notifyObservers();
}
//注册一个观察者
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
//移除一个观察者
@Override
public void removeObserver(Observer o) {
if(observers.contains(o)) {
observers.remove(o);
}
}
//遍历所有的观察者,并通知
@Override
public void notifyObservers() {
for(int i = 0; i < observers.size(); i++) {
observers.get(i).update(this.temperatrue, this.pressure, this.humidity);
}
}
}
平台接口
//观察者接口,有观察者来实现
public interface Observer {
public void update(float temperature, float pressure, float humidity);
}
百度平台
public class BaiduSite implements Observer {
// 温度,气压,湿度
private float temperature;
private float pressure;
private float humidity;
// 更新 天气情况,是由 WeatherData 来调用,我使用推送模式
public void update(float temperature, float pressure, float humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
}
// 显示
public void display() {
System.out.println("===百度网站====");
System.out.println("***百度网站 气温 : " + temperature + "***");
System.out.println("***百度网站 气压: " + pressure + "***");
System.out.println("***百度网站 湿度: " + humidity + "***");
}
}