一篇文章搞定《实战中的设计模式之Android版》

news2025/1/31 22:49:32

一篇文章搞定《实战中的设计模式之Android版》

  • 前言
  • 单例设计模式
    • 模式选用说明
    • 场景复现:
  • 构建者设计模式
    • 模式选用说明
    • 场景复现
  • 工厂设计模式
    • 模式选用说明
    • 场景复现
  • 策略设计模式
    • 模式选用说明
    • 场景复现
  • 装饰者设计模式
    • 模式选用说明
    • 场景复现
  • 适配器设计模式
    • 模式选用说明
    • 场景复现
  • 总结

前言

其实大多数可能和我一样,在开发项目的累积经验下,和对设计模式隐约的记忆下。
在开发项目的过程中其实已经使用到了设计模式,但是不能自知。

比如:之前开发的基于AI的一个对话IM,里面涉及到了很多的设计模式。但是都是下意识的去使用,甚至连他是那种设计模式都有些模糊。
(ps:就单例模式记得最熟)

本篇呢,也是对之前在IM开发中,遇到的一些设计模式,进行了认真的总结。也希望读者们可以恍然大悟!!!
哎!!!! 原来我这块代码用到了XXX设计模式。
下面就依次的对我使用到的设计模式进行剖析(代码被我简化了)。

单例设计模式

模式选用说明

说明:这个非常常见的设计模式,相信大家都用过的。几乎系统中的Manager类都是通过单例实现
使用场景:对于频繁的创建、销毁的对象,并且想全局的保留这个类的状态的时候,你就给我用上!!!!!
目的: 确保一个类只有一个实例,并提供该实例的全局访问点
设计模式类别:创建型模式

场景复现:

那必须是一个网络请求的管理类

  • 需要频繁地创建和销毁对象
  • 需要全局访问
  • 保存一些登录凭证、用户信息等共享的数据,方便其他模块进行使用

咱就是说,这不是妥妥的需要用单例模式的场景吗
先说一下小编最喜欢用的几种实现(JAVA and Kotlin ps:完全是因为写着顺手并且适用于所有场景)
其他单例的实现方式,详细的请看设计模式之《单例设计模式》
Java(静态内部类的方式)

public class OkHttpManager {
    OkHttpManager(){}

    private static class OkHttpManagerHandle{
        static OkHttpManager INSTANCE = new OkHttpManager();
    }

    public static OkHttpManager getInstance(){
        return OkHttpManagerHandle.INSTANCE;
    }
}

Java(双重检验锁)

private volatile static OkHttpManager INSTANCE;

OkHttpManager(){}

public static OkHttpManager getInstance(){
    if (INSTANCE == null){
        synchronized (OkHttpManager.class){
            if (INSTANCE == null){
                INSTANCE = new OkHttpManager();
            }
        }
    }
    return INSTANCE;
}

Kotlin(饿汉式)

object OkHttpManager { }

Kotlin(双重检验锁)

class OkHttpManager {
    companion object {
        @Volatile
        private var INSTANCE: OkHttpManager? = null

        fun getInstance(): OkHttpManager {
            return INSTANCE ?: synchronized(this) {
                return INSTANCE ?: OkHttpManager().also { INSTANCE = it }
            }
        }
    }
}

构建者设计模式

构建者模式这个有点太常见了,比较大的框架比如OkHttp,Glide都利用了构建者模式去创建对象,这样方便于使用者能够进行一定的自定义。

模式选用说明

说明:使用多个简单的对象一步一步构建成一个复杂的对象。
使用场景:当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数,并且一些基本部件不会变的对象,就考虑使用构造者模式。
目的:解决复杂对象的构建
设计模式类别:创建型模式

场景复现

那必然是自定义的Dialog啊兄弟们,项目中需要大量的使用到Dialog,IM模块中也是必不可少的。那原声的不满足要求,但是你也不能遇到一个场景就去自定义一个Dialog啊。
我们直接自定义一个全局使用的,之后通过构建者模式去控制它的功能、样式、事件的处理不就行了
开干!!!

  • 第一步:继承Dialog并初始化
class CustomDialog private constructor(
    context: Context,
    private val title: String?,
    private val content: String?,
    private val positiveButton: String?,
    private val negativeButton: String?,
    private val cancelable: Boolean,
    private val backgroundColor: Int
) : Dialog(context) {

    init {
        setContentView(createView())
        setCancelable(cancelable)
    }
    
    private fun createView(): View {
        //这部分省略了就是设置一些布局相关的,当然你可以自定义布局
    }
  • 第二步:利用静态内部类,构建链式调用设置属性
class Builder(private val context: Context) {
    private var title: String? = null
    private var content: String? = null
    private var positiveButton: String? = null
    private var negativeButton: String? = null
    private var cancelable: Boolean = true
    private var backgroundColor: Int = Color.WHITE

    fun setTitle(title: String): Builder {
        this.title = title
        return this
    }

    //一些set方法就省略了

    fun setBackgroundColor(backgroundColor: Int): Builder {
        this.backgroundColor = backgroundColor
        return this
    }

    fun build(): CustomDialog {
        return CustomDialog(
            context,
            title,
            content,
            positiveButton,
            negativeButton,
            cancelable,
            backgroundColor
        )
    }
}
  • 第三步:链式调用构建对象
val dialog = CustomDialog.Builder(context)
    .setTitle("Dialog Title")
    .setContent("Dialog Content")
    .setPositiveButton("OK")
    .setNegativeButton("Cancel")
    .setCancelable(true)
    .setBackgroundColor(Color.YELLOW)
    .build()

dialog.show()

当然这只是举例子,真实项目中的利用构建者自定义的Dialog,每个内容都是支持自定义的,包括确定和取消按钮的布局样式。

工厂设计模式

先说一下工厂模式和上面的策略模式的区别,以便大家别看懵圈了,因为两种模式特别的相似。

  • 区别一:用途不一样
    • 工厂模式是创建型模式,他是创建对象的。
    • 策略是行为型模式,他是让已经创建对象去选择一个属于自己的方法行为的。
  • 区别二:使用时候不同
    • 工厂方法模式调用方可以直接调用工厂方法实例的方法属性等。
    • 策略模式不能直接调用实例的方法属性,需要在策略类中封装策略后调用。
  • 区别总结:也就是说工厂是通过创建对象来多样化,策略是通过选择行为来多样化。
  • 结合使用:即工厂模式可以用来创建多样化的策略对象。

模式选用说明

说明:这是一种很常见的创建型的设计模式,这种方式使我们的对象的创建与使用代码分离,他不会向外层暴露内部对象的创建逻辑
使用场景:设计到多种类型的对象创建,并且这多种类型都是一个业务场景的。你就给我改成工厂模式
目的:创建产品对象的工厂接口,让其子类决定实例化哪一个类,将实际创建工作推迟到子类当中
设计模式类别:创建型模式

场景复现

多种类型的Message消息,我想统一的使用HandleMessage方法。
因为对于IM消息,我肯定会涉及到获取内部的方法属性等等。所以我这里选择工厂设计模式。策略模式只会去handle我的消息而已。
原代码场景:

  • 根据不同类型的IM消息,去实例化不同的对象,去调用HandleMessage
  • 比如图片、视频、文本、语音
if (msgType = "文本") {
    TextMessageStrategy().handleMessage(messageData)
} else if(msgType = "图片") {
    ImageMessageStrategy().handleMessage(messageData)
} else if(msgType = "视频") {
    VideoMessageStrategy().handleMessage(messageData)
} else {
    DefaultMessageStrategy().handleMessage(messageData)
}

这时候你想想,当你的IM类型多了起来,这段代码是多么难受,并且如果多人去开发不同的IM消息类型,大家都要去操作这个段代码。一点解偶性都没有。
如果你想在外层操作这个代码,就要把你创建的对象抛出去。
利用工厂模式进行改进:

  • 第一步:创建统一处理handleMessage接口
interface IMessageHandle {
    //属于哪种文件解析类型 MessageTypeResolveEnum 用枚举类定义的IM类型
    fun getMessageType(): MessageTypeResolveEnum?

    //具体的处理消息方法
    fun handleMessage(messageData: BaseMessageData?)
}
  • 第二步:去统一实现IMessageHandle
// 文本消息
class TextMessage : IMessageHandle {
    //文本类型
    override fun getMessageType(): MessageTypeResolveEnum {
        return MessageTypeResolveEnum.TEXT
    }
    //处理文本
    override fun handleMessage(messageData: BaseMessageData?) {
        println("处理文本消息 " + messageData.getContent())
    }
}


//图片的消息
class ImageMessage: IMessageHandle {
    override fun getMessageType(): MessageTypeResolveEnum? {
        MessageTypeResolveEnum.IMAGE
    }

    override fun handleMessage(messageData: BaseMessageData?) {
        println("处理图片消息 " + messageData.getContent())
    }
}

//其他默认消息
class DefaultMessage: IMessageHandle {
    override fun getMessageType(): MessageTypeResolveEnum? {
        MessageTypeResolveEnum.DEFAULT
    }

    override fun handleMessage(messageData: BaseMessageData?) {
        println("处理其他默认消息 " + messageData.getContent());
    }
}
  • 第三步:创建工厂
class IMessageFactory {
    companion object {
        fun parse(type: Int): IMessageStrategy? {
            //其他统一处理
            return parseIMessage(type)
        }

        private fun parseIMessage(type: Int): IMessageStrategy? {
            var message: IMessageStrategy? = null
            when (type) {
                MessageTypeResolveEnum.TEXT.type -> {
                    message = TextMessageStrategy()
                }

                MessageTypeResolveEnum.IMAGE.type -> {
                    message = TextMessageStrategy()
                }

                MessageTypeResolveEnum.DEFAULT.type -> {
                    message = TextMessageStrategy()
                }
            }
            return message;
        }
    }
}
  • 第四步:使用
val messageData: BaseMessageData = BaseMessageData("hahaha", MessageTypeResolveEnum.TEXT)
val message = IMessageFactory.parse(MessageTypeResolveEnum.TEXT.type)
IMAdapter.addItem(message)
message?.getMessageType()
message?.handleMessage(messageData)

策略设计模式

策略模式用的肯定是要比工厂模式少一些的,因为多策略的场景没那么多。
最常见的几种:不同会员等级对商品进行打折处理、多文件类型的处理、日志的多种类控制台输出日志、文件记录日志、数据库记录日志等。
其实这里对会员的策略讲解起来比较清晰,但是本篇文章是总结我开发的IM的,所以就说说对文件类型的不同策略处理吧。

模式选用说明

说明:我想使得一个类的行为或其算法可以在运行时更改。
使用场景:一个系统有许多许多类,而区分它们的只是他们直接的行为(如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题)
目的:在有多种算法相似的情况下,解决大量的if else 的判断
设计模式类别:行为型模式

这里再次强调和工厂设计模式的区别,因为上面的IM消息我会涉及到内部的消息属性,所以我使用了工厂设计模式。
那什么场景下用策略设计模式呢?
答:只涉及到,你想区分这个类,仅仅是他的行为不同。

场景复现

比如我现在有几类的文件消息:表格、PDF、WORD、流程图等等多种类文件。那么他们如果在展示过程中都是相同的,都在消息中显示为文件。但是我点击后的打开方式不同。 那么我们还要去创建多种文件对象?他只有打开方式不同,其他都相同。
那么这个时候是不是就符合了我们的策略模式解决的问题,他只是打开的行为不同。
原代码

if (fileType = "表格文件") {
    // dosomething
} else if(fileType = "PDF文件") {
    // doshomething
} else if(fileType = "WORD文件") {
    // doshomething
} else {
    // doshomething
}

如果还有区分会员非会员,还有会员等级等等。那么这不是就多起来了吗,那怎么办?
利用策略设计模式进行改进:

  • 第一步:定义统一策略接口
interface FileMessageStrategy {
    //属于哪种文件解析类型 FileMessageTypeEnum 用枚举类定义的IM类型
    fun getFileType(): FileMessageTypeEnum?

    //封装的公用算法(具体的打开方法)
    fun openFile(messageData: BaseMessageData?)
}
  • 第二步:不同消息的处理策略实现
// 表格文件消息
class TableMessageStrategy : FileMessageStrategy {
    //表格类型
    override fun getFileType(): FileMessageTypeEnum {
        return FileMessageTypeEnum.TABLE
    }
    //打开表格文件
    override fun openFile(messageData: BaseMessageData?) {
        println("打开表格文件 " + messageData.getContent())
    }
}

// PDF文件消息
class PDFMessageStrategy : FileMessageStrategy {
    //表格类型
    override fun getFileType(): FileMessageTypeEnum {
        return FileMessageTypeEnum.PDF
    }
    //打开PDF文件
    override fun openFile(messageData: BaseMessageData?) {
        println("打开PDF文件 " + messageData.getContent())
    }
}


// word文件消息
class WordMessageStrategy : FileMessageStrategy {
    //word类型
    override fun getFileType(): FileMessageTypeEnum {
        return FileMessageTypeEnum.WORD
    }
    //打开word文件
    override fun openFile(messageData: BaseMessageData?) {
        println("打开word文件 " + messageData.getContent())
    }
}
  • 第三步:将文件类型和相关处理进行绑定
class BaseFileMessageStrategy {
    private var messageMap: MutableMap<FileMessageTypeEnum, FileMessageStrategy> = ConcurrentHashMap()

    // 处理消息
    fun openFile(messageData: BaseMessageData) {
        val fileMessageStrategy: FileMessageStrategy? = messageMap[messageData.type]
        fileMessageStrategy?.let {
            it.openFile(messageData)
        }
    }

    // 添加消息处理策略
    fun putFileMessageStrategy(messageType: FileMessageTypeEnum, fileMessageStrategy: FileMessageStrategy) {
        messageMap[messageType] = fileMessageStrategy
        //可以通过工厂模式创建具体的策略对象,工厂模式负责根据需求创建不同的策略实例。
        //这样,在策略模式中可以通过工厂模式动态切换具体的策略,并且可以随时添加新的策略。
        //具体在下面通过工厂模式去讲,就是通过工厂去按照messageType去创建messageStrategy
    }
}

//使用
BaseFileMessageStrategy().openFile(messageData)

装饰者设计模式

简单的认为,他就是扩展一个类的功能。那么有人问了,你扩展功能,用继承不就行了吗?
可以增加方法,重写方法。 那确实是这么回事,继承确实可以去实现你要扩展功能的要求。
但是你会遇到下面几个问题:

  • 强依赖、强固定的问题
    • 当你不断的有新的功能进行扩展,你就要不断的去往下继承子类吗? 你想想到时候那一串子的子类,到时候维护和查看的难度,看了想死。
    • 当你再后期的维护中有想,移除某个中间迭代的功能,这时候因为你是通过继承不断的传递下来了,这改一下子,难受的要死,甚至对其他功能还会有影响。
  • 单继承问题
    • 万一你要扩展的这个类,还要继承其他的类,比如一个抽象的功能类。咋办?
    • 装饰者就可以规避这个问题
  • 运行时的动态添加
    • 当你选用继承关系的时候,你当前的功能就在编译时被固定了,运行时无法限制。
    • 当你选用装饰者设计模式的时候,你可以在运行动态的对要扩展的功能进行添加
      上面就是为什么要选择装饰者模式,而不用继承的原因了。

模式选用说明

说明:加强版的继承扩展类功能的方式,比如Android中的RecyclerView.Adapter。
使用场景:扩展一个类的功能,并且后续还有扩展功能的需求。或者不能用继承去扩展这个类的功能的时候。
目的:动态的扩展一个类的功能,并且不想添加大量子类。
设计模式类别:结构型模式

场景复现

就是说我现在有一个订单功能,其中有一个基础的订单类Order。有以下几个常用的方法

  • 获取订单ID
  • 获取订单金额
public class Order {
    private String orderId;
    private double totalAmount;

    public Order(String orderId, double totalAmount) {
        this.orderId = orderId;
        this.totalAmount = totalAmount;
    }

    public String getOrderId() {
        return orderId;
    }

    public double getTotalAmount() {
        return totalAmount;
    }

    public void process() {
        System.out.println("Processing order: " + orderId);
    }
}

我现在想扩展两个功能,分了两次需求来的。那么我不想通过继承,而是装饰者去扩展。

  • 第一步:创建基础的装饰者类
public abstract class OrderDecorator extends Order {
    protected Order decoratedOrder;

    public OrderDecorator(Order decoratedOrder) {
        super(decoratedOrder.getOrderId(), decoratedOrder.getTotalAmount());
        this.decoratedOrder = decoratedOrder;
    }

    public void process() {
        decoratedOrder.process();
    }
}
  • 第二步:扩展第一个功能,订单日志记录
public class LoggedOrder extends OrderDecorator {
    public LoggedOrder(Order decoratedOrder) {
        super(decoratedOrder);
    }

    public void process() {
        log();
        super.process();
    }

    private void log() {
        System.out.println("Logging order: " + decoratedOrder.getOrderId());
    }
}
  • 扩展第二个内容:发送邮件通知
public class NotificationOrder extends OrderDecorator {
    public NotificationOrder(Order decoratedOrder) {
        super(decoratedOrder);
    }

    public void process() {
        super.process();
        sendNotification();
    }

    private void sendNotification() {
        System.out.println("Sending notification for order: " + decoratedOrder.getOrderId());
    }
}
  • 第三步:使用,可以动态的切换两个功能去使用
Order order = new Order("12345", 100.0);
Order decoratedOrder;
if (version == "4.0"){
    decoratedOrder = new LoggedOrder(order);
} else {
    decoratedOrder = new NotificationOrder(order);
}
decoratedOrder.process();

看到没,通过使用装饰者设计模式,我们可以动态地为订单对象添加不同的功能,而无需修改订单类或创建大量的子类。极大的增加了可扩展性和灵活性。

适配器设计模式

适配器模式其实大家最常见了,那就是我们常用的列表RecyclerView,他的Adapter适配器。
适配器模式旨在:使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
对于RecyclerView你可以这么去想:你的数据是现有的系统、你要把数据转换成View,View就是你的厂商类,那么这个时候Adapter就是适配器。也就是GetView这个函数去将不同的数据和当前View进行绑定。
这个图太形象了。
在这里插入图片描述

模式选用说明

说明:使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。也就是我们所说的中间层,也就是接口不兼容的情况。
使用场景:需要一个统一的输出接口,而输入端的接口不可预知
目的:适配接口,让不可预测的输入端,统一的输出。
设计模式类别:结构型模式

场景复现

1、我们正在开发一个支付系统,我们需要适配两个新加入的第三方支付方式
2、但是我们最后只想通过之前的接口去统一支付
3、这时候我们可以创建一个适配器类,把这两第三方的支付接口,适配到之前原有的支付接口上去
大家说我用if else 也可以实现。 那确实,那代码看起来就像粑粑了,而且随着你支付方式的增多,当中途有支付方式的修改,增加,减少就会越来越变得难以维护。这个就叫做耦合。
来看看适配器怎么做吧。

  • 之前统一的支付接口
public interface Payment {
    void pay(float amount);
}
  • 新加入的两个三方支付接口:比如是微信、支付宝
//微信
public class WeChatPay implements WeChatPayPayment {
    @Override
    public void weChatPayPay(float amount) {
        // 具体的支付逻辑
        System.out.println("Using WeChat Pay to pay: " + amount);
    }
}

//支付宝
public class Alipay implements AlipayPayment {
    @Override
    public void alipayPay(float amount) {
        // 具体的支付逻辑
        System.out.println("Using Alipay to pay: " + amount);
    }
}
  • 第一步:建立适配器类PaymentAdapter,用于适配所有支付方式:(这种是类适配器的方式)
public class PaymentAdapter implements Payment {
    private Object payment;

    public PaymentAdapter(Object payment) {
        this.payment = payment;
    }

    @Override
    public void pay(float amount) {
        if (payment instanceof AlipayPayment) {
            ((AlipayPayment) payment).alipayPay(amount);
        } else if (payment instanceof WeChatPayPayment) {
            ((WeChatPayPayment) payment).weChatPayPay(amount);
        }
        // 其他支付方式的调用...
    }
}
  • 第二步:使用适配器
//阿里支付
Alipay alipay = new Alipay();
Payment payment = new PaymentAdapter(alipay);
payment.pay(100.0f);

//微信支付        
WeChatPay weChatPay = new WeChatPay();
payment = new PaymentAdapter(weChatPay);
payment.pay(200.0f);

当然知识去演示了一下,如何使用这个适配器,真实环境下。你的payment.pay是在主类里的。肯定不会去动。
而各种支付方式就会变成入参进来。
所以需要统一接口去用,你通过适配器将入参传递进来就可以。这样就避免了去修改支付主类。

总结

一切的设计模式,都是为了解决耦合性、扩展性、复用性、可测试性。
它并不是,为了解决一些代码问题。所有的需要通过任何方式都可以实现,并不是必须使用设计模式。
但是!!!!
设计模式在较大项目中,就能体现出来他的优势,耦合性、扩展性、复用性、可测试性让后续的维护和开发大大减少了时间。
减少的时间在于:不用担心牵一发动全身(耦合性)、你不用关心内部实现(扩展性)、拿来就用(复用性)、快速的单元测试(可测试性)。

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

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

相关文章

C++--动态规划其他问题

1.一和零 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 给你一个二进制字符串数组 strs 和两个整数 m 和 n 。 请你找出并返回 strs 的最大子集的长度&#xff0c;该子集中 最多 有 m 个 0 和 n 个 1 。 如果 x 的所有元素也是 y 的元素&#xff0…

如何使用Cygwin编译最新版的Redis源码,生成适用于Windows的Redis

文章目录 一、准备Cygwin环境二、下载Redis源码三、编译redis-7.2.01. 执行make命令2. 重新执行make命令3. 再次执行make命令4. 将编译后的可执行文件及依赖放到同一个文件夹5. 测试编译生成的可执行程序 四、换其他redis版本重新编译1. 编译redis-7.0.122. 编译redis-6.2.133.…

算法:分治思想处理归并递归问题

文章目录 算法原理实现思路典型例题排序数组数组中的逆序对计算右侧小于当前元素的个数 总结 算法原理 利用归并思想进行分治也是很重要的一种思路&#xff0c;在解决逆序对的问题上有很大的需求空间 于是首先归并排序是首先的&#xff0c;归并排序要能写出来&#xff1a; c…

还在苦恼如何开发一个Chrome插件吗?十分钟带你实现一个实用小插件

你是否曾考虑过创建自己的 Chrome 插件&#xff0c;但又挠头毫无思路&#xff1f;那么在接下来的几分钟里&#xff0c;我不仅会介绍 Chrome 浏览器扩展的基本知识&#xff0c;还会指导你通过五个简单的步骤来制作自己的扩展。 知道怎么做吗&#xff1f;让我们一探究竟&#xff…

探索在云原生环境中构建的大数据驱动的智能应用程序的成功案例,并分析它们的关键要素。

文章目录 1. Netflix - 个性化推荐引擎2. Uber - 实时数据分析和决策支持3. Airbnb - 价格预测和优化5. Google - 自然语言处理和搜索优化 &#x1f388;个人主页&#xff1a;程序员 小侯 &#x1f390;CSDN新晋作者 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 ✨收录专…

EG1164大功率同步整流升压模块开源,最高效率97%

EG1164大功率同步整流Boost升压电源模块&#xff0c;最高效率97%&#xff0c;输入电压8~50V&#xff0c;输出电压8~60V可调&#xff0c;最大功率300瓦以上&#xff0c;开关频率219kHz。 白嫖了张嘉立创的彩色丝印券就随便画了个板试试&#xff0c;第一次打彩色丝印。 因为我测…

word导出为HTML格式教程,同时也导出图片

在写文档教程时&#xff0c;有时需要借鉴人家的专业文档内容&#xff0c;一般都是word格式文档。word直接复制里面的内容&#xff0c;帐帖到网站编辑器会有很多问题&#xff0c;需要二次清楚下格式才行&#xff0c;而且图片是没办法直接复制到编辑器内的。所以最方便的办法是将…

ARM编程模型-指令流水线

流水线技术通过多个功能部件并行工作来缩短程序执行时间&#xff0c;提高处理器核的效率和吞吐率&#xff0c;从而成为微处理器设计中最为重要的技术之一。 1. 3级流水线 到ARM7为止的ARM处理器使用简单的3级流水线&#xff0c;它包括下列流水线级。 &#xff08;1&#xff0…

如何中mac上安装多版本python并配置PATH

摘要 mac 默认安装的python是 python3&#xff0c;但是如果我们需要其他python版本时&#xff0c;该怎么办呢&#xff1f; 例如&#xff1a;需要python2 版本&#xff0c;如果使用homebrew安装会提示没有python2。同时使用python --version 会发现commond not found。 所以本…

linux安装minio以及springboot整合使用

文章目录 1.linux安装minio2.springboot整合minio使用 1.linux安装minio 1.新建文件夹 mkdir /home/minio # 数据文件夹 mkdir /home/minio/data # 创建日志文件夹 mkdir /home/minio/log2.进入文件夹 cd /home/minio3.下载minio&#xff0c;链接可从官网获取 wget https://…

有c语言的基础学习python【python基础详解】

文章将从C语言出发&#xff0c;深入介绍python的基础知识&#xff0c;也包括很多python的新增知识点详解。 目录 1.python的输入输出&#xff0c;重新认识 hello world 1.1 输出函数print的规则 1.2 输入函数input的规则 1.3 用print将数据写入文件 2.数据类型、基本操作…

SPSS统计作图教程:百分条图堆积条图

1、问题与数据 某研究者想看不同年龄分组人群&#xff08;Age_cat&#xff09;中不同程度的维生素D缺乏&#xff08;VD&#xff09;的百分构成比&#xff0c;部分数据如图1。研究者想以条图形式来展现&#xff0c;该如何操作呢&#xff1f; 图1 部分数据 2. 具体操作&#xf…

不用加减乘除做加法

不用加减乘除做加法 一、描述二、方法 一、描述 写一个函数&#xff0c;求两个整数之和&#xff0c;要求在函数体内不得使用、-、*、/四则运算符号&#xff0c;本题OJ链接 数据范围&#xff1a;两个数都满足 −10≤n≤1000 进阶&#xff1a;空间复杂度 O(1)&#xff0c;时间复…

构造函数和析构函数(个人学习笔记黑马学习)

构造函数:主要作用在于创建对象时为对象的成员属性赋值&#xff0c;构造函数由编译器自动调用&#xff0c;无须手动调用。析构函数:主要作用在于对象销毁前系统自动调用&#xff0c;执行一些清理工作。 #include <iostream> using namespace std;//对象初始化和清理class…

MVC,MVP,MVVM的理解和区别

MVC MVC &#xff0c;早期的开发架构&#xff0c;在安卓里&#xff0c;用res代表V&#xff0c;activity代表Controller层&#xff0c;Model层完成数据请求&#xff0c;更新操作&#xff0c;activity完成view的绑定&#xff0c;以及业务逻辑的编写&#xff0c;更新view&#xf…

2359. 找到离给定两个节点最近的节点;1781. 所有子字符串美丽值之和;2406. 将区间分为最少组数

2359. 找到离给定两个节点最近的节点 核心思想:统计node1和node2分别到每个点的距离&#xff0c;然后在枚举每个点统计结果。关键在于如何统计node到每个点的距离&#xff0c;首先是初始化为inf很重要&#xff0c;因为在枚举的时候&#xff0c;因为是inf代表了这个节点无法到达…

MVC模式分层练习

新建库 新建表 插入点数据 先不用MVC模式写功能,来看下缺点是什么 新建一个空项目 选项项目使用的JDK 自己的IDEA总是要重启下 新建模块 因maven还没教 添加框架支持 添加后项目多了这些 添加些必要依赖 这里注意下,如果导入jar包不对可以重新导入下或者是jar包本身出了问…

设计模式-2--工厂模式(Factory Pattern)

一、 什么是工厂模式 工厂模式&#xff08;Factory Pattern&#xff09;是一种创建型设计模式&#xff0c;它提供了一种创建对象的接口&#xff0c;但是将对象的实例化过程推迟到子类中。工厂模式允许通过调用一个共同的接口方法来创建不同类型的对象&#xff0c;而无需暴露对…

yolov5 应用整理

yolov5 应用整理 适用于0基础人员与有一定yolov5了解的人员食用. 关于yolov5参考: yolov5 github源码链接 目前参与过yolov5的应用: 平台库x86/arm cpuncnnx86libtorch/pytorchBM1684算能标准库(需要进行模型转换)昇腾cann(ascend api) https://gitee.com/Tencent/ncnn ht…

在windows下安装配置skywalking

1.下载地址 Downloads | Apache SkyWalkinghttp://skywalking.apache.org/downloads/ 2.文件目录说明 将文件解压后&#xff0c;可看到agent和bin目录&#xff1a; Agent&#xff1a;作为探针&#xff0c;安装在服务器端&#xff0c;进行数据采集和上报。 Config&#xff1a…