设计模式

本文介绍了设计模式的概念及其七大基本原则,并详细解析了创建型模式中的单例模式、工厂方法模式、抽象工厂模式、建造者模式及原型模式。同时,还探讨了结构型模式中的适配器模式和装饰模式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、设计模式的概念

设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。

二、设计模式的七大原则

1、单一职责原则 ( SRP )

英文全称是Single Responsibility Principle,定义是一个类,应该只有一个引起它变化的原因。类变化的原因就是职责,如果一个类承担的职责过多,就等于把这些职责耦合在一起了。一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当发生变化时,设计会遭受到意想不到的破坏。而如果想要避免这种现象的发生,就要尽可能的遵守单一职责原则。此原则的核心就是解耦和增强内聚性。

2、开闭原则 ( OCP )

英文全称是Open Close Principle,定义是软件实体(包括类、模块
、函数等)应该对于扩展时开放的,对于修改是封闭的。开闭原则是是面向对象设计中最重要的原则之一,其它很多的设计原则都是实现开闭原则的一种手段。

3、里氏替换原则 ( LSP )

英文全称是Liskov Substitution Principle,是面向对象设计的基本原则之一。 定义是任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏替换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏替换原则是对实现抽象化的具体步骤的规范。

4、依赖倒置原则 ( DIP )

英文全称是Dependence Inversion Principle,这个原则是开闭原则的基础,依赖倒置原则就是要求调用者和被调用者都依赖抽象,这样两者没有直接的关联和接触,在变动的时候,一方的变动不会影响另一方的变动。依赖倒置强调了抽象的重要性,针对接口编程,依赖于抽象而不依赖于具体。

5、接口隔离原则 ( ISP )

英文全称是Interface Segregation Principle,这个原则的意思是使用多个隔离的接口,比使用单个接口要好。目的就是降低类之间的耦合度,便于软件升级和维护。

6、最少知道原则(迪米特原则)

一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。通俗地说就是不要和陌生人说话,即一个对象应对其他对象有尽可能少的了解。迪米特法则的初衷在于降低类之间的耦合。由于每个类尽量减少对其他类的依赖,因此,很容易使得系统的功能模块功能独立,相互之间不存在(或很少有)依赖关系。

7、合成/聚合复用(CARP)

英文全称是Composite Reuse Principle,合成/聚合复用原则经常又叫做合成复用原则。合成/聚合复用原则的潜台词是:我只是用你的方法,我们不一定是同类。继承的耦合性更大,比如一个父类后来添加实现一个接口或者去掉一个接口,那子类可能会遭到毁灭性的编译错误,但如果只是组合聚合,只是引用类的方法,就不会有这种巨大的风险,同时也实现了复用。

三、创建型模式 

1.单例模式

确保某一个类只有一个实例,并自行实例化向整个系统提供这个实例。

单例模式理解起来不难,典型例子有一个公司只能有一个CEO。它主要是为了保证一个类仅有一个实例,这个类中自己提供一个返回实例的方法,方法中先判断系统是否已经有这个单例,如果有则返回,如果没有则创建。如果创建多个实例会消耗过多的资源或者某种类型的对象只应该有且只有一个时,应该考虑使用单例模式。

写法一、懒汉式写法

public class Singleton {
 
    private static Singleton instance; 

    //构造函数私有
    private Singleton (){
    }  

    public static synchronized Singleton getInstance() { 
         if (instance == null) {  
             instance = new Singleton();  
         }  
         return instance;  
    }  
}  
写法二、饿汉式
public class Singleton {  
    private final static Singleton instance = new Singleton();  
    private Singleton (){}  
    public static Singleton getInstance() {  
    return instance;  
    }  
}

写法三、DCL(Double Check Lock) 双重校验锁

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 Singleton (){
    }  

    public static final Singleton getInstance() {  
          return SingletonHolder.INSTANCE;  
    }  

    private static class SingletonHolder {  
         private static final Singleton INSTANCE = new Singleton();  
    }
}   

写法五、枚举
public enum Singleton {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
}

优点:

  • 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
  • 2、避免对资源的多重占用(比如写文件操作)。

缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

使用场景:

  • 1、要求生产唯一序列号。
  • 2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
  • 3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。

2.工厂方法模式

定义一个用于创建对象的接口,让子类决定实例化哪一个类。

工厂方法模式的典型例子,自行车分为山地自行车和公路自行车等,当需要买自行车时,我们直接去自行车厂里告诉厂长我们需要的自行车即可。

1、定义一个接口自行车,Bike。

public interface Bike{ 
    void ride();
}

2、定义实现类山地自行车,MBike

public class MBike implements Bike{ 
    @Override 
    public void ride() { 
           System.out.println("MBike Rides... "); 
    }
}

3、定义实现类公路自行车,RBike

public class RBike implements Bike{ 
    @Override 
    public void ride() { 
           System.out.println("RBike Rides... "); 
    }
}

4、工厂类,负责创建对象
public class BikeFactory {
   //使用 getBikr 方法获取形状类型的对象
   public Bike getBike(String bikeType){

      if(bikeType == null){
         return null;
      }     

      if(bikeType.equalsIgnoreCase("MBike")){
         return new MBike();
      }else if(shapeType.equalsIgnoreCase("RBike")){
         return new RBike();
      }

      return null;
   }
}

5、子类决定实例化哪一个类

public class FactoryPatternDemo {
   public static void main(String[] args) {

      BikeFactory bikeFactory = new BikeFactory();

      Bike bike1 = bikeFactory.getBike("MBike");
      bike1.ride();

      Bike bike2= bikeFactory.getBike("RBike");
      bike2.ride();
   }
}

优点: 1、一个调用者想创建一个对象,只要知道其名称就可以了。 2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 3、屏蔽产品的具体实现,调用者只关心产品的接口。

缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。

3.抽象工厂模式

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

抽象工厂模式主要解决接口选择的问题,典型例子是手机和电脑问题,假设抽象产品1是手机,具体产品是Android手机和iOS手机,抽象产品2是电脑,具体产品是Windows电脑和Mac电脑,抽象工厂是同时生产手机和电脑的公司,具体工厂是联想公司和苹果公司。

抽象工厂的优点是当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。缺点是产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象工厂里加代码,又要在具体工厂里加代码。

4.建造者模式

将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。

建造者模式主要解决在软件系统中一个复杂对象的创建工作。通常一个复杂对象是由各个部分的子对象用一定的算法构成,由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。

建造者模式的一个典型例子是Android中的AlertDialog的构建过程。还有个例子是计算机的组装,计算机是个复杂的对象,它是有很多零件组装而成,显示器、操作系统,鼠标等,通过创建Builder接口来控制零件的组装过程,这样当组件发生变化时,虽然经过同样的构建过程,但是最后得到的结果不同。

5.原型模式

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

原型模式不难理解,它主要是用在实例创建的时候,因为有的时候我们通过new创建一个对象时可能成本过高,这时我们可以考虑直接通过直接克隆实例快速创建对象。克隆后的实例与原实例内部属性一致。原型模式需要注意一个深拷贝和浅拷贝的问题。

四、结构型模式

1.适配器模式

将一个类的接口转换成另外一个客户希望的接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

适配器模式包括两种类型,类适配器模式和对象适配器模式。这里主要介绍对象适配器模式,因为它更灵活。适配器模式中主要有三个部分,Target、Adapter和Adaptee,其中Target是目标角色,Adaptee是需要被转换的对象,Adapter就是适配器。例如,现在有个手机充电电压是5v,而插座电压是220v,这时我们可以把充电器看成是Adapter,它将电压进行转化最后得到结果。

// Target 目标接口
public interface Volt5 {
    public int getVolt5();
}

// Adaptee 需要被转换的对象
public class Volt220 {
    public int getVolt220() {
        return 220;
    }
}

//Adapter 适配器 
public class VoltAdapter implements Volt5 {

    Volt220 mVolt220;

    public VoltAdapter(Volt220 adaptee) {
        mVolt220 = adaptee;
    }

    public int getVolt220() {
        return 220;
    }

    @Override
    public int getVolt5() {
        return 5;
    }
}

2.装饰模式

动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。

装饰模式是作为现有的类的一个包装,它允许向一个现有的对象添加新的功能,同时又不改变其结构。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。装饰模式主要是装饰器类Decorator的设计,它对指定对象进行装饰。

基于数据挖掘的音乐推荐系统设计与实现 需要一个代码说明,不需要论文 采用python语言,django框架,mysql数据库开发 编程环境:pycharm,mysql8.0 系统分为前台+后台模式开发 网站前台: 用户注册, 登录 搜索音乐,音乐欣赏(可以在线进行播放) 用户登陆时选择相关感兴趣的音乐风格 音乐收藏 音乐推荐算法:(重点) 本课题需要大量用户行为(如播放记录、收藏列表)、音乐特征(如音频特征、歌曲元数据)等数据 (1)根据用户之间相似性或关联性,给一个用户推荐与其相似或有关联的其他用户所感兴趣的音乐; (2)根据音乐之间的相似性或关联性,给一个用户推荐与其感兴趣的音乐相似或有关联的其他音乐。 基于用户的推荐和基于物品的推荐 其中基于用户的推荐是基于用户的相似度找出相似相似用户,然后向目标用户推荐其相似用户喜欢的东西(和你似的人也喜欢**东西); 而基于物品的推荐是基于物品的相似度找出相似的物品做推荐(喜欢该音乐的人还喜欢了**音乐); 管理员 管理员信息管理 注册用户管理,审核 音乐爬虫(爬虫方式爬取网站音乐数据) 音乐信息管理(上传歌曲MP3,以便前台播放) 音乐收藏管理 用户 用户资料修改 我的音乐收藏 完整前后端源码,部署后可正常运行! 环境说明 开发语言:python后端 python版本:3.7 数据库:mysql 5.7+ 数据库工具:Navicat11+ 开发软件:pycharm
MPU6050是一款广泛应用在无人机、机器人和运动设备中的六轴姿态传感器,它集成了三轴陀螺仪和三轴加速度计。这款传感器能够实时监测并提供设备的角速度和线性加速度数据,对于理解物体的动态运动状态至关重要。在Arduino平台上,通过特定的库文件可以方便地与MPU6050进行通信,获取并解析传感器数据。 `MPU6050.cpp`和`MPU6050.h`是Arduino库的关键组成部分。`MPU6050.h`是头文件,包含了定义传感器接口和函数声明。它定义了`MPU6050`,该包含了初始化传感器、读取数据等方法。例如,`begin()`函数用于设置传感器的工作模式和I2C地址,`getAcceleration()`和`getGyroscope()`则分别用于获取加速度和角速度数据。 在Arduino项目中,首先需要包含`MPU6050.h`头文件,然后创建`MPU6050`对象,并调用`begin()`函数初始化传感器。之后,可以通过循环调用`getAcceleration()`和`getGyroscope()`来不断更新传感器读数。为了处理这些原始数据,通常还需要进行校准和滤波,以消除噪声和漂移。 I2C通信协议是MPU6050与Arduino交互的基础,它是一种低引脚数的串行通信协议,允许多个设备共享一对数据线。Arduino板上的Wire库提供了I2C通信的底层支持,使得用户无需深入了解通信细节,就能方便地与MPU6050交互。 MPU6050传感器的数据包括加速度(X、Y、Z轴)和角速度(同样为X、Y、Z轴)。加速度数据可以用来计算物体的静态位置和动态运动,而角速度数据则能反映物体转动的速度。结合这两个数据,可以进一步计算出物体的姿态(如角度和角速度变化)。 在嵌入式开发领域,特别是使用STM32微控制器时,也可以找到似的库来驱动MPU6050。STM32通常具有更强大的处理能力和更多的GPIO口,可以实现更复杂的控制算法。然而,基本的传感器操作流程和数据处理原理与Arduino平台相似。 在实际应用中,除了基本的传感器读取,还可能涉及到温度补偿、低功耗模式设置、DMP(数字运动处理器)功能的利用等高级特性。DMP可以帮助处理传感器数据,实现更高级的运动估计,减轻主控制器的计算负担。 MPU6050是一个强大的六轴传感器,广泛应用于各种需要实时运动追踪的项目中。通过 Arduino 或 STM32 的库文件,开发者可以轻松地与传感器交互,获取并处理数据,实现各种创新应用。博客和其他开源资源是学习和解决问题的重要途径,通过这些资源,开发者可以获得关于MPU6050的详细信息和实践指南
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值