03.如何实现一个遥控器-命令模式

简介: 03.如何实现一个遥控器-命令模式


阅读目录

原文地址:03.命令模式

设计模式-文章目录

   01.策略模式-上篇

   02.单件模式

   03.命令模式

回到顶部

本篇所有示例程序下载:

【设计模式】03.命令模式_博客园jackson0714.zip

 

命令模式将请求封装成对象,这可以让你使用不同的请求,这可以让你使用不同的请求、队列,或者日志请求来参数化其他对象。命令模式也可以支持撤销操作。当需要将发出请求的对象和执行请求的对象解耦的时候,使用命令模式。

 

在本章,我们将把封装带到一个全新的境界:把方法调用(method invocation封装起来。没错,通过封装方法调用,我们可以把运算块包装成形。所以调用此运算的对象不需要关心事情是如何进行的,只要知道如何使用包装成形的方法来完成它就可以。通过封装调用方法调用,也可以做一些很聪明的事情,例如记录日志,或者重复使用这些封装来实现撤销(undo

 

实例分析:

让硬件解脱,让我们看看遥控器

有七个插槽需要编程,可以在每个插槽放上不同的装置,然后用按钮控制它。

这七个插槽具备各自的“开”与“关”按钮。

图片.png

 

一、简单遥控器

1.实现电灯类

using System;
namespace Command
{
    public class Light
    {
        public void On()
        {
            Console.WriteLine("Light is On");
        }
        public void Off()
        {
            Console.WriteLine("Light is Off");
        }
    }
}

2.实现命令接口

namespace Command
{
    public interface ICommand
    {
        public void execute();
    }
}

3.实现一个打开电灯的命令

namespace Command
{
    public class LightOnCommand : ICommand
    {
        private Light _light = null;
        public LightOnCommand(Light light)
        {
            this._light = light;
        }
        public void execute()
        {
            _light.On();
        }
    }
}

4.假设我们只有一个遥控器,它只有一个按钮和对应的插槽,可以控制一个装置:

namespace Command
{
    public class SimpleRemoteControl
    {
        private ICommand _slot;
        public SimpleRemoteControl()
        { 
        }
        public void SetCommand(ICommand command)
        {
            _slot = command;
        }
        public void ButtonWasPressed()
        {
            _slot.execute();
        }
    }
}

 

5.测试上面的简单遥控器,控制电灯的打开

using System;
namespace Command
{
    class Program
    {
        static void Main(string[] args)
        {
            SimpleRemoteControl remote = new SimpleRemoteControl();
            Light light = new Light();
            LightOnCommand lightOn = new LightOnCommand(light);
            remote.SetCommand(lightOn);
            remote.ButtonWasPressed();
            Console.ReadKey();
        }
    }
}

6.实现仓库门类。

using System;
namespace Command
{
    public class GarageDoor
    {
        public void Up()
        { Console.WriteLine("Garage door is Open"); }
        public void Close()
        { Console.WriteLine("Garage door is Close"); }
        public void Stop()
        { Console.WriteLine("Garage door is Stop"); }
        public void LightOn()
        { Console.WriteLine("Garage door light is On"); }
        public void LightOff()
        { Console.WriteLine("Garage door light is Off"); }
    }
}

7.实现打开仓库门的类

using System;
namespace Command
{
    public class GarageDoor
    {
        public void Up()
        { Console.WriteLine("Garage door is Open"); }
        public void Close()
        { Console.WriteLine("Garage door is Close"); }
        public void Stop()
        { Console.WriteLine("Garage door is Stop"); }
        public void LightOn()
        { Console.WriteLine("Garage door light is On"); }
        public void LightOff()
        { Console.WriteLine("Garage door light is Off"); }
    }
}

8.测试打开电灯和打开仓库门的遥控器

using System;
namespace Command
{
    class Program
    {
        static void Main(string[] args)
        {
            SimpleRemoteControl remote = new SimpleRemoteControl();
            Light light = new Light();
            LightOnCommand lightOn = new LightOnCommand(light);
            GarageDoor garageDoor = new GarageDoor();
            GarageDoorOpenCommand garageDoorOpen = new GarageDoorOpenCommand(garageDoor);
            remote.SetCommand(lightOn);
            remote.ButtonWasPressed();
            remote.SetCommand(garageDoorOpen);
            remote.ButtonWasPressed();
            Console.ReadKey();
        }
    }
}

 

测试结果:

图片.png

 

二、复杂遥控器

1.实现遥控器

using System.Text;
namespace Command
{
    class RemoteControl
    {
        ICommand[] onCommands;
        ICommand[] offCommands;
        public RemoteControl()
        {
            onCommands = new ICommand[7];
            offCommands = new ICommand[7];
            ICommand noCommand = new NoCommand();
            for (int i = 0; i < 7; i++)
            {
                onCommands[i] = noCommand;
                offCommands[i] = noCommand;
            }
        }
        public void SetCommand(int slot, ICommand onCommand, ICommand offCommand)
        {
            onCommands[slot] = onCommand;
            offCommands[slot] = offCommand;
        }
        public void OnButtonWasPushed(int slot)
        {
            onCommands[slot].Execute();
        }
        public void OffButtonWasPushed(int slot)
        {
            offCommands[slot].Execute();
        }
        public string ToString()
        {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append("\n-----Remote Control-----\n");
            for (int i = 0; i < onCommands.Length; i++)
            {
                stringBuilder.Append("\r\n[slot " + i + "]");
                stringBuilder.Append(onCommands[i].GetType());
                stringBuilder.Append("    ");
                stringBuilder.Append(offCommands[i].GetType());
            }
            return stringBuilder.ToString();
        }
    }
}

2.实现NoCommand

namespace Command
{
    class NoCommand:ICommand
    {
        public void Execute()
        {
        }
    }
}

3.实现命令

(1)实现关闭电灯命令

namespace Command
    {
        public class LightOffCommand : ICommand
        {
            private Light _light = null;
            public LightOffCommand(Light light)
            {
                this._light = light;
            }
            public void Execute()
            {
                _light.Off();
            }
        }
    }

(2)实现关闭仓库门命令

namespace Command
    {
        class GarageDoorDownCommand:ICommand
        {
            private GarageDoor _garageDoor = null;
            public GarageDoorDownCommand(GarageDoor garageDoor)
            {
                _garageDoor = garageDoor;
            }
            public void Execute()
            {
                _garageDoor.Down();
            }
        }
    }

4.测试复杂遥控器

//02.复杂控制器--------------------------------------
            Console.WriteLine("\n-----02.复杂控制器-----\n");
            RemoteControl remoteControl = new RemoteControl();
            remoteControl.SetCommand(0, lightOn, lightOff);
            remoteControl.SetCommand(1, garageDoorOpen, garageDoorDown);
            Console.WriteLine(remoteControl.ToString());
            remoteControl.OnButtonWasPushed(0);
            remoteControl.OffButtonWasPushed(0);
            remoteControl.OnButtonWasPushed(1);
            remoteControl.OffButtonWasPushed(1);

测试结果:

图片.png

三、实现Undo功能

1.当命令支持撤销时,该命令就必须提供和Execute()方法相反的Undo()方法。

不管Execute()刚才做什么,Undo()都会倒转过来。这么一来,在各个命令中加入Undo之前,我们必须先在Command接口中加入Undo()方法:


namespace Command
    {
        public interface ICommand
        {
            void Execute();
            void Undo();
        }
    }

2.我们从LightOnCommand开始下手

如果LightOnCommandExecute()方法被调用,那么最后调用的是On()方法。我们知道Undo()需要调用Off()方法进行相反的动作。

namespace Command
    {
        public class LightOnCommand : ICommand
        {
            private Light _light = null;
            public LightOnCommand(Light light)
            {
                this._light = light;
            }
            public void Execute()
            {
                _light.On();
            }
            public void Undo()
            {
                _light.Off();
            }
        }
    }

3.要加上对撤销按钮的莪支持,我们必须对遥控器类做一些小修改。

加入一个新的实例变量,用来追踪最后被调用的命令,然后,不管何时撤销按钮被按下,我们都可以取出这个命令并调用它的Undo()方法。

using System.Text;
    namespace Command
    {
        class RemoteControlWithUndo
        {
            ICommand[] onCommands;
            ICommand[] offCommands;
            ICommand undoCommand;
            public RemoteControlWithUndo()
            {
                onCommands = new ICommand[7];
                offCommands = new ICommand[7];
                ICommand noCommand = new NoCommand();
                for (int i = 0; i < 7; i++)
                {
                    onCommands[i] = noCommand;
                    offCommands[i] = noCommand;
                }
                undoCommand = noCommand;
            }
            public void SetCommand(int slot, ICommand onCommand, ICommand offCommand)
            {
                onCommands[slot] = onCommand;
                offCommands[slot] = offCommand;
            }
            public void OnButtonWasPushed(int slot)
            {
                onCommands[slot].Execute();
                undoCommand = onCommands[slot];
            }
            public void OffButtonWasPushed(int slot)
            {
                offCommands[slot].Execute();
                undoCommand = offCommands[slot];
            }
            public void UndoButtonWasPushed()
            {
                undoCommand.Undo();
            }
            public string ToString()
            {
                StringBuilder stringBuilder = new StringBuilder();
                stringBuilder.Append("\n-----Remote Control With Undo-----\n");
                for (int i = 0; i < onCommands.Length; i++)
                {
                    stringBuilder.Append("\r\n[slot " + i + "]");
                    stringBuilder.Append(onCommands[i].GetType());
                    stringBuilder.Append("    ");
                    stringBuilder.Append(offCommands[i].GetType());
                }
                return stringBuilder.ToString();
            }
        }
    }

4.测试具有撤销功能的复杂控制器

//03.复杂控制器,撤销功能--------------------------------------
                Console.WriteLine("\n-----03.复杂控制器,撤销功能-----\n");
                RemoteControlWithUndo remoteControlWithUndo = new RemoteControlWithUndo();
                remoteControlWithUndo.SetCommand(0, lightOn, lightOff);
                remoteControlWithUndo.OnButtonWasPushed(0);
                remoteControlWithUndo.OffButtonWasPushed(0);
                Console.WriteLine(remoteControlWithUndo.ToString());
                remoteControlWithUndo.UndoButtonWasPushed();
                remoteControlWithUndo.OffButtonWasPushed(0);
                remoteControlWithUndo.OnButtonWasPushed(0);
                Console.WriteLine(remoteControlWithUndo.ToString());
                remoteControlWithUndo.UndoButtonWasPushed();

测试结果:

图片.png

 

回到顶部

四、复杂撤销

1.使用状态实现撤销

实现电灯的撤销是有意义的,但也实在是太容易了。通常,想要实现撤销的功能,需要记录一些状态,让我们试一个更有趣的例子,比方说厂商类中的天花板上的吊扇。吊扇允许有多种转动速度,当然也允许被关闭。

吊扇的源代码如下:

namespace Command.Model
    {
        class CeilingFan
        {
            public static readonly int HighSpeed = 3;
            public static readonly int  MediumSpeed = 2;
            public static readonly int LowSpeed = 1;
            public static readonly int OffSpeed = 0;
            private string _location;
            private int _speed;
            public CeilingFan(string location)
            {
                _location = location;
                _speed = OffSpeed;
            }
            public void SetHighSpeed()
            {
                _speed = HighSpeed;
            }
            public void SetMediumSpeed()
            {
                _speed = MediumSpeed;
            }
            public void SetLowSpeed()
            {
                _speed = LowSpeed;
            }
            public void SetOffSpeed()
            {
                _speed = OffSpeed;
            }
            public int GetSpeed()
            {
                return _speed;
            }
        }
    }

2.加入撤销到吊扇的命令类

现在就让我们把撤销加入到天花板吊扇的诸多命令中。这么做,需要追踪吊扇的最后设置速度,如果undo()方法被调用了,就要恢复成之前吊扇的速度的设置值。

下面是CeilingFanHighCommand的代码:

namespace Command
    {
        class CeilingFanHighCommand : ICommand
        {
            private CeilingFan _ceilingFan;
            private int _prevSpeed;
            public CeilingFanHighCommand(CeilingFan ceilingFan)
            {
                _ceilingFan = ceilingFan;
            }
            public void Execute()
            {
                _prevSpeed = _ceilingFan.GetSpeed();//在改变吊扇速度之前,需要先将它之前的状态记录起来,以便需要撤销时使用。
                _ceilingFan.SetHighSpeed();
            }
            public void Undo()
            {
                if (_prevSpeed == CeilingFan.HighSpeed)
                {
                    _ceilingFan.SetHighSpeed();
                }
                else if (_prevSpeed == CeilingFan.MediumSpeed)
                {
                    _ceilingFan.SetMediumSpeed();
                }
                if (_prevSpeed == CeilingFan.LowSpeed)
                {
                    _ceilingFan.SetLowSpeed();
                }
                if (_prevSpeed == CeilingFan.OffSpeed)
                {
                    _ceilingFan.SetOffSpeed();
                }
            }
        }
    }

下面是CeilingFanMediumCommand的代码:

namespace Command
    {
        class CeilingFanMediumCommand : ICommand
        {
            private CeilingFan _ceilingFan;
            private int _prevSpeed;
            public CeilingFanMediumCommand(CeilingFan ceilingFan)
            {
                _ceilingFan = ceilingFan;
            }
            public void Execute()
            {
                _prevSpeed = _ceilingFan.GetSpeed();//在改变吊扇速度之前,需要先将它之前的状态记录起来,以便需要撤销时使用。
                _ceilingFan.SetMediumSpeed();
            }
            public void Undo()
            {
                if (_prevSpeed == CeilingFan.HighSpeed)
                {
                    _ceilingFan.SetHighSpeed();
                }
                else if (_prevSpeed == CeilingFan.MediumSpeed)
                {
                    _ceilingFan.SetMediumSpeed();
                }
                if (_prevSpeed == CeilingFan.LowSpeed)
                {
                    _ceilingFan.SetLowSpeed();
                }
                if (_prevSpeed == CeilingFan.OffSpeed)
                {
                    _ceilingFan.SetOffSpeed();
                }
            }
        }
    }

3.测试天花板吊扇

把第0号插槽的开启按钮设置为中苏,把第一号插槽的开启按钮设置为高度,而两个对应的关闭按钮,都是关闭吊扇的命令。

测试脚本如下:

//04.复杂撤销功能-天花板吊扇
                    Console.WriteLine("\n-----04.复杂撤销功能-天花板吊扇-----\n");
                    remoteControlWithUndo = new RemoteControlWithUndo();
                    CeilingFan ceilingFan = new CeilingFan("Living Room");
                    CeilingFanHighCommand ceilingFanHighCommand = new CeilingFanHighCommand(ceilingFan);
                    CeilingFanMediumCommand ceilingFanMediumCommand = new CeilingFanMediumCommand(ceilingFan);
                    CeilingFanOffCommand ceilingFanOffCommand = new CeilingFanOffCommand(ceilingFan);
                    remoteControlWithUndo.SetCommand(0,ceilingFanHighCommand,ceilingFanOffCommand);//0号插槽的On键设置为高速,Off键设置为关闭
                    remoteControlWithUndo.SetCommand(1,ceilingFanMediumCommand,ceilingFanOffCommand);//1号插槽的On键设置为中速,Off键设置为关闭
                    remoteControlWithUndo.OnButtonWasPushed(0);//首先以高速开启吊扇
                    remoteControlWithUndo.OffButtonWasPushed(0);//然后关闭
                    Console.WriteLine(remoteControlWithUndo.ToString());
                    remoteControlWithUndo.UndoButtonWasPushed();//撤销,回到中速
                    remoteControlWithUndo.OnButtonWasPushed(1);//开启中速
                    remoteControlWithUndo.OffButtonWasPushed(1);//关闭
                    Console.WriteLine(remoteControlWithUndo.ToString());//撤销,回到中速
                    remoteControlWithUndo.UndoButtonWasPushed();

 

测试结果:

图片.png

回到顶部

五、每个遥控器都需具备“Party模式”!

如果拥有了一个遥控器,却无法光凭按下一个按钮,就同时能弄暗灯管,打开音响和电视,设置好DVD,并让热水器开始加温,那么要这个遥控器还有什么意义?

1.制造一个新的命令,用来执行其他一堆命令,而不只是执行一个命令。

namespace Command
        {
            class MacroCommand : ICommand
            {
                private ICommand[] _commands;
                public MacroCommand(ICommand[] commands)
                {
                    _commands = commands;
                }
                public void Execute()
                {
                    for (int i = 0; i < _commands.Length; i++)
                    {
                        _commands[i].Execute();
                    }
                }
                public void Undo()
                {
                    for (int i = _commands.Length - 1; i >= 0; i--)
                    {
                        _commands[i].Undo();
                    }
                }
            }
        }

2.使用宏命令

            ICommand[] partyOn = {lightOn, garageDoorOpen, ceilingFanHighCommand};//一个数组用来记录开启命令
                    ICommand[] partyOff = {lightOff, garageDoorDown, ceilingFanOffCommand};//另一个数组用来记录关闭命令
                    MacroCommand partyOnMacroCommand = new MacroCommand(partyOn);//创建对应的宏持有开启命令数组
                    MacroCommand partyOffMacroCommand = new MacroCommand(partyOff);//创建对应的宏持有关闭命令数组
            remoteControlWithUndo.SetCommand(0,partyOnMacroCommand,partyOffMacroCommand);//将宏命令指定一个按钮

3.测试Party模式

//05.Party模式
                    Console.WriteLine("\n-----05.Party模式-----\n");
                    ICommand[] partyOn = {lightOn, garageDoorOpen, ceilingFanHighCommand};//一个数组用来记录开启命令
                    ICommand[] partyOff = {lightOff, garageDoorDown, ceilingFanOffCommand};//另一个数组用来记录关闭命令
                    MacroCommand partyOnMacroCommand = new MacroCommand(partyOn);//创建对应的宏持有开启命令数组
                    MacroCommand partyOffMacroCommand = new MacroCommand(partyOff);//创建对应的宏持有关闭命令数组
                    remoteControlWithUndo = new RemoteControlWithUndo();
                    remoteControlWithUndo.SetCommand(0,partyOnMacroCommand,partyOffMacroCommand);//将宏命令指定一个按钮
                    Console.WriteLine(remoteControlWithUndo);
                    Console.WriteLine("----Pushing Macro On----");
                    remoteControlWithUndo.OnButtonWasPushed(0);
                    Console.WriteLine("----Pushing Macro Off----");
                    remoteControlWithUndo.OffButtonWasPushed(0);
                    Console.WriteLine("----Pushing Macro Undo----");
                    remoteControlWithUndo.UndoButtonWasPushed();

 

测试结果:

图片.png

回到顶部

六、问答

1.接收者一定有必要存在吗?为何命令对象不直接实现execute()方法的细节?

答:一般来说,我们尽量设计傻瓜命令对象,它只懂得调用一个接收者的一个行为。然后,有许多“聪明”命令对象会实现许多逻辑,直接完成一个请求。

当然你可以设计聪明的命令对象,只是这样一来,调用者和接收者之间的解耦程度是比不上“傻瓜”命令对象的,而且,你也不能够直接把接收者当作参数传递给命令。

2.我如何能够实现多层次的撤销操作?换句话说,我希望能够按下撤销按钮许多次,撤销到很早很早以前的状态。

答:好问题!其实这相当容易做到,不要只是记录最后一个被执行的命令,而使用一个堆栈记录操作过程的每一个命令。然后,不管什么时候按下了撤销按钮,你都可以从堆栈中去除最上层的命令,然后调用它的undo()方法。

3.我可以创建一个Party-Command,然后再它的execute()方法中调用其他的命令,利用这种做法实现Party模式吗?

答:你可以这么做。然而,这等于把Party模式硬编码到PartyCommand中。

回到顶部

七、命令模式的更多用途

1.队列请求

想象有一个工作队列:你在某一端添加命令,然后另一端则是线程。线程进行下面的动作:从队列中中取出一个命令,调用它的execute()方法,等待这个调用完成,然后将此命令对象对象丢弃,再取出下一个命令。

2.日志请求。

某些应用需要我们将所有的动作都记录在日志中,并能在系统司机之后,重新调用这些动作恢复到之前的状态。通过新增两个方法(Store()Load()

回到顶部

八、要点

1.命令模式将发出请求的对象和执行请求的对象解耦。

2.在被解耦的两者之间是通过命令对象进行沟通的。命令对象封装了接收者和一个或一组动作。

3.调用者通过调用命令对象的execute()发出请求,这会使得接收者的动作被调用。

4.调用者可以接受命令当作参数,甚至在运行时动态地进行。

5.命令可以支持撤销,做法是实现一个Undo()方法来回到Execute()被执行钱的状态。

6.宏命令是命令的一种简单的延伸,允许调用多个命令。宏方法也可以支持撤销。

7.实际操作时,很常见的使用“聪明”命令对象,也就是直接实现了请求,而不是将工作委托给接收者,这种实现方式并不能将请求的对象和执行请求的对象解耦。

7.命令也可以用来实现日志和事物系统。

参考文献:

《Head First设计模式》




相关实践学习
通过日志服务实现云资源OSS的安全审计
本实验介绍如何通过日志服务实现云资源OSS的安全审计。
相关文章
|
存储 Java Windows
Java21 JDK下载安装及Windows环境变量配置
JDK是Java的开发工具包,要进行Java学习或开发之前,需先下载安装,下载地址如下:提示:这网址里面有三个扩展名的文件,分别是“.zip”、“.exe”和“.msi”,鄙人选择的是.exe的文件,下方的安装和环境的配置也是安装该文件的安装程序进行的。
3038 2
|
Android开发
解决圆形进度条ProgressBar的几个问题
Android自带的Progressbar默认就是圆形的,可以通过设置style属性 style="?android:attr/progressBarStyleHorizontal" 复制代码 这样就能变成条状进度条,如下: <ProgressBar android:layout_width="match_parent" android:layout_height="wrap_content" style="?android:attr/progressBarStyleHorizontal"/>
1608 0
|
9月前
|
人工智能 运维 架构师
技能革命3.0时代:云计算就业岗位有哪些?
本文探讨云计算就业市场的深层逻辑,从岗位体系、AI赋能及技术局限性应对策略三方面解析。云计算岗位涵盖基础设施、平台服务、数据智能与应用创新四层,需复合型能力;AI不仅提供技术工具,还推动教育范式变革,助力跨界融合;面对技术局限,分步验证与经验洞察双管齐下。未来就业将向技能多元化、自主性增强和社会价值再定义方向进化,强调个体能力生态的持续成长。
|
缓存 Java 数据库连接
Spring框架中的事件机制:深入理解与实践
Spring框架是一个广泛使用的Java企业级应用框架,提供了依赖注入、面向切面编程(AOP)、事务管理、Web应用程序开发等一系列功能。在Spring框架中,事件机制是一种重要的通信方式,它允许不同组件之间进行松耦合的通信,提高了应用程序的可维护性和可扩展性。本文将深入探讨Spring框架中的事件机制,包括不同类型的事件、底层原理、应用实践以及优缺点。
347 8
|
Web App开发 安全 程序员
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
多年的互联网寒冬在今年尤为凛冽,坚守安卓开发愈发不易。面对是否转行或学习新技术的迷茫,安卓程序员可从三个方向进阶:1)钻研谷歌新技术,如Kotlin、Flutter、Jetpack等;2)拓展新功能应用,掌握Socket、OpenGL、WebRTC等专业领域技能;3)结合其他行业,如汽车、游戏、安全等,拓宽职业道路。这三个方向各有学习难度和保饭碗指数,助你在安卓开发领域持续成长。
281 1
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
|
设计模式 XML 存储
【二】设计模式~~~创建型模式~~~工厂方法模式(Java)
文章详细介绍了工厂方法模式(Factory Method Pattern),这是一种创建型设计模式,用于将对象的创建过程委托给多个工厂子类中的某一个,以实现对象创建的封装和扩展性。文章通过日志记录器的实例,展示了工厂方法模式的结构、角色、时序图、代码实现、优点、缺点以及适用环境,并探讨了如何通过配置文件和Java反射机制实现工厂的动态创建。
【二】设计模式~~~创建型模式~~~工厂方法模式(Java)
|
机器学习/深度学习 人工智能 缓存
【AI系统】GPU 架构回顾(从2018年-2024年)
2018年发布的Turing图灵架构,采用12nm工艺,包含18.6亿个晶体管,大幅提升了PC游戏、专业图形应用及深度学习推理的效率与性能。Turing引入了RT Core和Tensor Core,分别用于实时光线追踪和加速深度学习计算,支持GDDR6内存,显著提升了数据传输速率和效率。此外,Turing架构还支持NVLink 2.0,增强了多GPU协同工作的能力,适用于复杂的图形渲染和深度学习任务。
767 0
【AI系统】GPU 架构回顾(从2018年-2024年)
|
NoSQL Linux
看懂GDB调试核心:剖析ptrace原理及其应用场景!(上)
看懂GDB调试核心:剖析ptrace原理及其应用场景!
|
监控 Java Docker
Spring Boot与Traefik的集成
Spring Boot与Traefik的集成
hutool工具分页工具
hutool工具分页工具

热门文章

最新文章