31--设计模式、面向对象设计原则

1、设计模式

1.1 设计模式概述

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。

设计模式不是一种方法和技术,而是一种思想。

设计模式和具体的语言无关,学习设计模式就是要建立面向对象的思想,尽可能的面向接口编程,低耦合,高内聚,使设计的程序可复用。

学习设计模式能够促进对面向对象思想的理解,反之亦然。它们相辅相成

简单一句话:设计模式就是经验的总结

设计模式的几个要素

  • 名字:必须有一个简单,有意义的名字
  • 问题描述:在何时使用模式
  • 解决方案:描述设计的组成部分以及如何解决问题
  • 效果:描述模式的效果以及优缺点设计模式的分类

设计模式的分类

  • 创建型模式:对象的创建
  • 结构型模式:对象的组成(结构)
  • 行为型模式:对象的行为

创建型模式:简单工厂模式,工厂方法模式,抽象工厂模式,建造者模式,原型模式,单例模式。(6个)

结构型模式:外观模式、适配器模式、代理模式、装饰模式、桥接模式、组合模式、享元模式。(7个)

行为型模式:模版方法模式、观察者模式、状态模式、职责链模式、命令模式、访问者模式、策略模式、备忘录模式、迭代器模式、解释器模式。(10个)

1.2 单例模式详解

1.2.1 什么是单例设计模式

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

1.2.2 单例模式特点

1、单例类只能有一个实例。也就是只有一个对象

2、单例类必须自己创建自己的唯一实例。 写单例构造方法是要私有的

3、单例类必须给所有其他对象提供这一实例。 在该方法中,提供一个方法,用于获取该对象

1.2.3 单例设计模式实现前提条件

私有构造方法

在本类的成员位置,创建出自己类对象

提供公共方法,返回创建的对象 ,该方法必须是静态的

1.2.4 单例模式饿汉式

package com.suyv.singleton;/*** @Author: 憨憨浩浩* @CreateTime: 2023-12-27 10:46* @Description: 饿汉式单例模式*/
public class A {// 2.定义变量接收类的对象private static A a = new A();// 1.私有化构造器private A(){}// 3.定义静态方法返回类的对象public static A getInstance(){return a;}}

测试

package com.suyv.singleton;/*** @Author: 憨憨浩浩* @CreateTime: 2023-12-27 10:49* @Description: 饿汉式单例模式测试*/
public class ADemo01 {public static void main(String[] args) {A a1 = A.getInstance();A a2 = A.getInstance();System.out.println(a1);     // com.suyv.singleton.A@677327b6System.out.println(a2);     // com.suyv.singleton.A@677327b6System.out.println(a1 == a2);       // true}
}

1.2.5 单例模式懒汉式

package com.suyv.singleton;/*** @Author: 憨憨浩浩* @CreateTime: 2023-12-27 10:52* @Description: 懒汉式单例模式*/
public class B {// 2.定义静态变量接收创建的对象private static B b;// 1.私有化构造器private B(){}// 3.定义一个静态方法,第一次创建时才创建对象,后面调用都会返回这一个对象public static B getInstance(){if (b == null){b = new B();}return b;}
}

测试

package com.suyv.singleton;/*** @Author: 憨憨浩浩* @CreateTime: 2023-12-27 10:56* @Description: 懒汉式单例模式测试*/
public class BDemo01 {public static void main(String[] args) {B b1 = B.getInstance();B b2 = B.getInstance();System.out.println(b1);     // com.suyv.singleton.B@677327b6System.out.println(b2);     // com.suyv.singleton.B@677327b6System.out.println(b1 == b2);       // true}
}

1.2.6 单例设计模式的线程安全问题

1.2.6.1 饿汉式没有线程安全问题

饿汉式:在类初始化时就直接创建单例对象,而类初始化过程是没有线程安全问题的

1.2.6.2 懒汉式线程安全问题

问题出现:

package com.suyv.singleton;/*** @Author: 憨憨浩浩* @CreateTime: 2023-12-27 10:52* @Description: 懒汉式单例模式*/
public class B{// 2.定义静态变量接收创建的对象private static volatile B b;// 1.私有化构造器private B(){}// 3.定义一个静态方法,第一次创建时才创建对象,后面调用都会返回这一个对象public static B getInstance(){if (b == null){try {// 让问题暴露更加明显Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}b = new B();}return b;}
}
package com.suyv.singleton;/*** @Author: 憨憨浩浩* @CreateTime: 2023-12-27 10:56* @Description: 懒汉式单例模式线程安全测试*/
public class BDemo02 {static B b1 = null;static B b2 = null;public static void main(String[] args) {Thread t1 = new Thread(){@Overridepublic void run() {b1 = B.getInstance();}};Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {b2 = B.getInstance();}});t1.start();t2.start();try {t1.join();} catch (InterruptedException e) {e.printStackTrace();}try {t2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(b1);     // com.suyv.singleton.B@677327b6System.out.println(b2);     // com.suyv.singleton.B@14ae5a5System.out.println(b1 == b2);       // false}
}

解决方案:

package com.suyv.singleton;/*** @Author: 憨憨浩浩* @CreateTime: 2023-12-27 10:52* @Description: 懒汉式单例模式*/
public class B{// 2.定义静态变量接收创建的对象private static volatile B b;// 1.私有化构造器private B(){}// 3.定义一个静态方法,第一次创建时才创建对象,后面调用都会返回这一个对象/*public static B getInstance(){if (b == null){try {// 让问题暴露更加明显Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}b = new B();}return b;}*/// 使用双重判断,效率更高public static B getInstance(){if (b == null){synchronized (B.class){if (b == null){b = new B();}}}return b;}
}
package com.suyv.singleton;/*** @Author: 憨憨浩浩* @CreateTime: 2023-12-27 10:56* @Description: 懒汉式单例模式线程安全测试*/
public class BDemo03 {static B b1 = null;static B b2 = null;public static void main(String[] args) {Thread t1 = new Thread(){@Overridepublic void run() {b1 = B.getInstance();}};Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {b2 = B.getInstance();}});t1.start();t2.start();try {t1.join();} catch (InterruptedException e) {e.printStackTrace();}try {t2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(b1);     // com.suyv.singleton.B@677327b6System.out.println(b2);     // com.suyv.singleton.B@677327b6System.out.println(b1 == b2);       // true}
}

解决程线安全问题

双层if判断的原因

  • 方法中加入同步锁,保证线程安全
  • 第二个线程调用方法getInstance()的时候,变量s,已经不是null,被前面的线程new过
  • 当已经有对象了,第二个线程没有必要再进入同步了,直接return返回对象即可

单例模式的应用场景和好处

        Runtime

        任务管理器对象、获取运行时对象

        使用单例模式,可以避免资源浪费。

1.3 工厂模式

工厂设计模式,属于创建型,用于对象的创建; 简单来说,就是专门生产对象的

1.3.1 简单工厂模式

package com.suyv.factory;/*** @Author: 憨憨浩浩* @CreateTime: 2023-12-27 11:26* @Description: 动物抽象类*/
public abstract class Animal {public abstract void eat();
}
package com.suyv.factory;/*** @Author: 憨憨浩浩* @CreateTime: 2023-12-27 11:27* @Description: 猫类*/
public class Cat extends Animal{@Overridepublic void eat() {System.out.println("猫吃鱼");}
}
package com.suyv.factory;/*** @Author: 憨憨浩浩* @CreateTime: 2023-12-27 11:28* @Description: 狗类*/
public class Dog extends Animal{@Overridepublic void eat() {System.out.println("狗吃骨头");}
}
package com.suyv.factory;/*** @Author: 憨憨浩浩* @CreateTime: 2023-12-27 11:29* @Description: 工厂设计类,这个类就是专门来生产对象,今后从这个工厂类中可以直接获取类对象*/
public class AnimalFactory {private AnimalFactory(){}public static Dog createDog(){return new Dog();}public static Cat creatCat(){return new Cat();}}
package com.suyv.factory;/*** @Author: 憨憨浩浩* @CreateTime: 2023-12-27 11:32* @Description: 工厂模式测试*/
public class AnimalTest {public static void main(String[] args) {// 工厂设计模式://     优点:我可以直接通过工厂来获取对象//     缺点:如果要加对象,需要修改工厂类,不便于后期的维护// 使用工厂模式创建对象Cat cat = AnimalFactory.creatCat();Dog dog = AnimalFactory.createDog();// 调用方法cat.eat();dog.eat();}
}

1.3.2 实例工厂模式

package com.suyv.factory1;/*** @Author: 憨憨浩浩* @CreateTime: 2023-12-27 12:11* @Description: 创建一个接口*/
public interface Product {public void doSomething();
}
package com.suyv.factory1;/*** @Author: 憨憨浩浩* @CreateTime: 2023-12-27 12:12* @Description: 实现接口的实现类*/
public class ConcreteProduct implements Product{@Overridepublic void doSomething() {System.out.println("Doing something in ConcreteProduct");}
}
package com.suyv.factory1;/*** @Author: 憨憨浩浩* @CreateTime: 2023-12-27 12:14* @Description: 实例工厂类*/
public class ProductFactory {public Product createProduct(){return new ConcreteProduct();}}
package com.suyv.factory1;/*** @Author: 憨憨浩浩* @CreateTime: 2023-12-27 12:15* @Description: 实例工厂测试类*/
public class ProductTest {public static void main(String[] args) {ProductFactory factory = new ProductFactory();Product product = factory.createProduct();product.doSomething();}
}

1.3.3 静态工厂模式

package com.suyv.staticfactory;/*** @Author: 憨憨浩浩* @CreateTime: 2023-12-27 12:21* @Description: 定义一个咖啡类*/
public abstract class Coffee {public abstract String getName();//加糖public void addsugar() {System.out.println("加糖");}//加奶public void addMilk() {System.out.println("加奶");}
}
package com.suyv.staticfactory;/*** @Author: 憨憨浩浩* @CreateTime: 2023-12-27 12:22* @Description: 美式咖啡*/
public class AmericanCoffee extends Coffee{@Overridepublic String getName() {return "美式咖啡";}
}
package com.suyv.staticfactory;/*** @Author: 憨憨浩浩* @CreateTime: 2023-12-27 12:23* @Description: 拿铁咖啡*/
public class LatteCoffee extends Coffee{@Overridepublic String getName() {return "拿铁咖啡";}
}
package com.suyv.staticfactory;/*** @Author: 憨憨浩浩* @CreateTime: 2023-12-27 12:29* @Description: 咖啡工厂类--静态工厂*/
public class SimpleCoffeeFactory {public static Coffee createCoffee(String type) {//声明Coffee类型的变量,根据不同类型创建不同的coffee子类对象Coffee coffee = null;if("american".equals(type)) {coffee = new AmericanCoffee();} else if("latte".equals(type)) {coffee = new LatteCoffee();} else {throw new RuntimeException("对不起,您所点的咖啡没有");}return coffee;}
}
package com.suyv.staticfactory;/*** @Author: 憨憨浩浩* @CreateTime: 2023-12-27 12:24* @Description: 咖啡店类*/
public class CoffeeStore {public Coffee orderCoffee(String type) {Coffee coffee = SimpleCoffeeFactory.createCoffee(type);//加配料coffee.addMilk();coffee.addsugar();return coffee;}
}
package com.suyv.staticfactory;/*** @Author: 憨憨浩浩* @CreateTime: 2023-12-27 12:26* @Description: 静态工厂类测试*/
public class CofferTest {public static void main(String[] args) {//创建咖啡店类对象CoffeeStore store = new CoffeeStore();Coffee coffee = store.orderCoffee("latte");System.out.println(coffee.getName());}
}

1.4 模板设计模式

模版方法模式,简称为模板设计模式,它主要思想是:把通用的代码生成一个模板,可以反复的使用

package com.suyv.template;/*** @Author: 憨憨浩浩* @CreateTime: 2023-12-27 14:00* @Description: 创建一个模板类*/
public abstract class GetTime {/*** @description: 获取一段程序的运行时间* @author: 憨憨浩浩* @date: 2023/12/27 14:02* @param: []* @return: long**/public long getTime(){long start = System.currentTimeMillis();// 程序code();long end = System.currentTimeMillis();return end - start;}public abstract void code();}
package com.suyv.template;/*** @Author: 憨憨浩浩* @CreateTime: 2023-12-27 14:03* @Description: 用户使用模板类*/
public class ForDemo extends GetTime{@Overridepublic void code() {for (int i = 0; i < 100; i++) {System.out.println(i);}}
}
package com.suyv.template;/*** @Author: 憨憨浩浩* @CreateTime: 2023-12-27 14:04* @Description: 模板设计模式测试类*/
public class TemplateTest {public static void main(String[] args) {ForDemo forDemo = new ForDemo();System.out.println(forDemo.getTime() + "毫秒");}
}

1.5 装饰设计模式

装饰设计模式: 增强原有对象的功能

原本有一个对象,但是这个对象的功能不够强,采用装饰设计模式,对原对象中功能进行增强

回想一下我们当时讲的缓冲流,其实就是装饰设计模式

package com.suyv.decorate;/*** @Author: 憨憨浩浩* @CreateTime: 2023-12-27 14:09* @Description: TODO*/
public class Phone {public void call(){System.out.println("手机打电话功能!");}
}
package com.suyv.decorate;/*** @Author: 憨憨浩浩* @CreateTime: 2023-12-27 14:10* @Description: TODO*/
public class SendMsg {private Phone phone;public SendMsg(Phone phone) {this.phone = phone;}//增强原有手机的功能public void msg(){System.out.println("发彩信");System.out.println("发短信");phone.call();}
}
package com.suyv.decorate;/*** @Author: 憨憨浩浩* @CreateTime: 2023-12-27 14:11* @Description: TODO*/
public class PhoneTest {public static void main(String[] args) {SendMsg sendMsg = new SendMsg(new Phone());sendMsg.msg();}
}

2、面向对象思想设计原则

面向对象思想设计原则

在实际的开发中,我们要想更深入的了解面向对象思想,就必须熟悉前人总结过的面向对象的思想的设计原则

  • 单一职责原则
  • 开闭原则
  • 里氏替换原则
  • 依赖注入原则
  • 接口分离原则
  • 迪米特原则

2.1 单一职责原则

其实就是开发人员经常说的"高内聚,低耦合”

也就是说,每个类应该只有一个职责,对外只能提供一种功能,而引起类变化的原因应该只有一个。在设计模式中,所有的设计模式都遵循这一原则。能自己完成的事就不要麻烦别人,把一件事细化细化,只做一件事情

能自己完成的事就不要麻烦别人,把一件事细化细化,只做一件事情

2.2 开闭原则 ocp

核心思想是:一个对象对扩展开放,对修改关闭。

其实开闭原则的意思就是:对类的改动是通过增加代码进行的,而不是修改现有代码。

也就是说软件开发人员一旦写出了可以运行的代码,就不应该去改动它,而是要保证它能一直运行下去,如何能够做到这一点呢?这就需要借助于抽象和多态,即把可能变化的内容抽象出来,从而使抽象的部分是相对稳定的,而具体的实现则是可以改变和扩展的

2.3 里氏替换原则

核心思想:在任何父类出现的地方都可以用它的子类来替代。

其实就是说:同一个继承体系中的对象应该有共同的行为特征。

2.4 依赖注入原则

核心思想:要依赖于抽象,不要依赖于具体实现。

其实就是说:在应用程序中,所有的类如果使用或依赖于其他的类,则应该依赖这些其他类的抽象类,而不是这些其他类的具体类。为了实现这一原则,就要求我们在编程的时候针对抽象类或者接口编程,而不是针对具体实现编程。

2.5 接口分离原则

核心思想:不应该强迫程序依赖它们不需要使用的方法。

其实就是说:一个接口不需要提供太多的行为,一个接口应该只提供一种对外的功能,不应该把所有的操作都封装到一个接口中。

2.6 迪米特原则

核心思想:一个对象应当对其他对象尽可能少的了解

其实就是说:降低各个对象之间的耦合,提高系统的可维护性。在模块之间应该只通过接口编程,而不理会模块的内部工作原理,它可以使各个模块耦合度降到最低,促进软件的复用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/580381.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

自定义删除无依赖文件的webpack插件

插件原理 通过自定义webpack插件&#xff0c;利用执行完成编译的封存阶段后&#xff0c;产生的产物module.fileDependencies&#xff0c;生成依赖的文件组。通过读文件的方式&#xff0c;将待扫描的文件组和有依赖关系的文件进行对比。最终暴露出项目中&#xff0c;不存在依赖…

ios 之 数据库、地理位置、应用内跳转、推送、制作静态库、CoreData

第一节&#xff1a;数据库 常见的API SQLite提供了一系列的API函数&#xff0c;用于执行各种数据库相关的操作。以下是一些常用的SQLite API函数及其简要说明&#xff1a;1. sqlite3_initialize:- 初始化SQLite库。通常在开始使用SQLite之前调用&#xff0c;但如果没有调用&a…

“京东”数据包暴雷——李逵还是李鬼?

大家好&#xff0c;我是吴军&#xff0c;一家软件技术开发公司的产品经理。 前几个月市面上出现了一个京东数据包的项目&#xff0c;乍一听还蛮正规的&#xff0c;强子不卖货&#xff0c;去做数据服务了&#xff1f;他究竟是怎么一个盈利方式&#xff1f;到底是李逵还是李鬼&a…

【新资讯】《网络安全事件报告管理办法(征求意见稿)》正在公开征求意见

近年来网络安全事故频发&#xff0c;造成了不少损失和危害。为了减少网络安全事故的发生&#xff0c;规范网络安全事件的报告&#xff0c;国家互联网信息办公室根据《中华人民共和国网络安全法》等法律法规起草了《网络安全事件报告管理办法&#xff08;征求意见稿&#xff09;…

释放创意,点亮视频!红巨星Magic Bullet Looks带给您绚丽的色彩魔法

Red Giant Magic Bullet Looks 是一款适用于Mac的视频后期处理软件。它是由Red Giant公司开发的一款专业级颜色校正和调色工具&#xff0c;旨在帮助电影制作人、视频编辑和摄影师实现令人惊叹的视觉效果。 Magic Bullet Looks 提供了一个直观而强大的用户界面&#xff0c;使用…

Linux操作系统(Crontab计划任务+NTP时间同步服务器)

如何修改linux系统时间 与时间相关的命令&#xff0c;查看当前的时间 运行 date 即可 cal 查看当前月份的日历 运行 timedatectl 查看时间详细参数 &#xff08; NTP&#xff1a; network time protocol 网络时间协议 &#xff09; &#xff08; local time : 本地时间 &#x…

volatile关键字详解

volatile 关键字详解 介绍 Java中的volatile关键字是一个用于确保变量在多线程环境下的可见性和部分有序性的修饰符。当一个字段被声明为volatile时&#xff0c;它具有以下特性&#xff1a; 可见性&#xff1a;在多线程环境中&#xff0c;一个线程修改了一个volatile变量的值…

采用curl -w测试接口或域名访问速度

命令&#xff1a; curl -o /dev/null -H "Cache-Control: no-cache" -s -w time_namelookup:"\t"%{time_namelookup}"\n"time_connect:"\t\t"%{time_connect}"\n"time_pretransfer:"\t"%{time_pretransfer}&quo…

BP算法与淋浴器的温度调节

BP算法&#xff08;反向传播算法&#xff09;是一种用于神经网络训练的基本算法。它通过逐层迭代地调整神经网络的权重和偏置&#xff0c;以使网络的输出尽可能接近期望的输出。BP算法之所以能够训练神经网络&#xff0c;是因为它基于梯度下降的思想&#xff0c;通过最小化损失…

大创项目推荐 深度学习LSTM新冠数据预测

文章目录 0 前言1 课题简介2 预测算法2.1 Logistic回归模型2.2 基于动力学SEIR模型改进的SEITR模型2.3 LSTM神经网络模型 3 预测效果3.1 Logistic回归模型3.2 SEITR模型3.3 LSTM神经网络模型 4 结论5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 …

11-网络安全框架及模型-软件安全能力成熟度模型(SSCMM)

目录 软件安全能力成熟度模型 1 背景概述 2 主要内容 3 成熟度等级定义 4 关键过程和实践 5 评估方法 6 改进建议 7 持续改进 8 主要价值 9 应用场景 10 优势和局限性 备注 软件安全能力成熟度模型 1 背景概述 SSCMM模型是软件安全能力成熟度模型&#xff0c;它描…

智能优化算法应用:基于鱼鹰算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于鱼鹰算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于鱼鹰算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.鱼鹰算法4.实验参数设定5.算法结果6.参考文献7.MA…

6、IDEA集成GitHub/码云

这里写目录标题 1、设置GitHub账号2、分享工程到GitHub3、Push推送本地库到远程库4、Pull拉取远程库到本地库5、Clone克隆远程库到本地6、码云简介 1、设置GitHub账号 可以采用两种登录方式&#xff1a;账户密码登入、口令登入。 2、分享工程到GitHub 先在GitHub中创建一个远…

【Hive_05】企业调优1(资源配置、explain、join优化)

1、 计算资源配置1.1 Yarn资源配置1.2 MapReduce资源配置 2、 Explain查看执行计划&#xff08;重点&#xff09;2.1 Explain执行计划概述2.2 基本语法2.3 案例实操 3、分组聚合优化3.1 优化说明&#xff08;1&#xff09;map-side 聚合相关的参数 3.2 优化案例 4、join优化4.1…

ARCGIS PRO SDK GeometryEngine处理独立几何图形的函数

1、面积类&#xff1a;pol为Polygon 1).Area&#xff1a;获取几何图形的面积。这是使用二维笛卡尔数学来计算面积的平面测量 double d GeometryEngine.Instance.Area(pol) 2).GeodesicArea:获取几何图形的椭球面积 …

highcharts的甘特图设置滚动时表头固定,让其他内容跟随滚动

效果图&#xff1a;最左侧的分类列是跟随甘特图滚动的&#xff0c;因为这一列如果需要自定义&#xff0c;比如表格的话可能会存在行合并的情况&#xff0c;这个时候甘特图是没有办法做的&#xff0c;然后甘特图的表头又需要做滚动时固定&#xff0c;所以设置了甘特图滚动时&…

Python测试框架pytest核心库pluggy详解

代码案例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 import pluggy # HookspecMarker 和 HookimplMarker 实质上是一个装饰器带参数的装饰器类&#xff0c;作用是给函数增加额外的…

Django查询每月的最后一条数据

查询每月的最后一条数据 在这个示例中&#xff0c;我们将演示如何使用Django查询Book数据表中姓名为"张三"的每月的最后一条数据。 数据模型 首先&#xff0c;我们定义了一个Book模型&#xff0c;具有以下字段&#xff1a; from django.db import modelsclass Bo…

【vue滚动条插件vuescroll】【vue自定义滚动条】

文章目录 前言一、使用步骤1.下载2.引入库三、在组件中如何使用&#xff1f;四、跳转到顶部的方法scrollTo() 五、效果总结 前言 由于浏览器自带的滚动条比较不符合设计图&#xff0c;所以在大部分项目中&#xff0c;我们都会自定义滚动条的样式&#xff0c;来还原设计图&…

策略模式(组件协作)

策略模式&#xff08;组件协作&#xff09; 链接&#xff1a;策略模式实例代码 注解 目的 正常情况下&#xff0c;一个类/对象中会包含其所有可能会使用的内外方法&#xff0c;但是一般情况下&#xff0c;这些常使用的类都是由不同的父类继承、组合得来的&#xff0c;来实现…