设计模式之迭代器模式与命令模式详解和应用

news2024/11/17 1:38:22

目录

  • 1 迭代器模式
    • 1.1 目标
    • 1.2 内容定位
    • 1.3 迭代器模式
    • 1.4 迭代器模式的应用场景
    • 1.5 手写字定义的送代器
    • 1.6 迭代器模式在源码中的体现
    • 1.7 迭代器模式的优缺点
  • 2 命令模式
    • 2.1 定义
    • 2.2 命令模式的应用场景
    • 2.3 命令模式在业务场景中的应用
    • 2.4 命令模式在源码中的体现
    • 2.5 命令模式的优缺点


1 迭代器模式

1.1 目标

1、 了解迭代器模式和命令的应用场景。

2、 自己手写迭代器

3、 掌握迭代器模式和命令模式在源码中的应用,知其所以然。

1.2 内容定位

听说过迭代器模式和命令模式,但并不知其所以然的人群。

1.3 迭代器模式

迭代器模式( Iterator Pattern ) 又称为游标模式(Cursor Pattern), 它提供一种顺序访问集合/ 容器对象元素的方法,而又无须暴露集合内部表示。迭代器模式可以为不同的容器提供一致的 遍历行为,而不用关心容器内容元素组成结构,属于行为型模式。

原文 : Provide a way to access the elements of an aggregate object sequentially without exposing its under lying representation.
解释:提供一种顺序访问集合/容器对象元素的方法,而又无须暴露集合内部表示。

迭代器模式的本质是抽离集合对象迭代行为到迭代器中,提供一致访问接口。

1.4 迭代器模式的应用场景

迭代器模式在我们生活中应用的得也比较广泛,比如物流系统中的传送带,不管传送的是什 么物品,都被打包成一个一个的箱子并且有一个统一的二维码。这样我们不需要关心箱子里面 是啥,我们在分发时只需要一个一个检查发送的目的地即可。再比如,我们平时乘坐交通工具, 都是统一刷卡或者刷脸进站,而不需要关心是男性还是女性、是残疾人还是正常人等个性化的信息。

在这里插入图片描述

我们把多个对象聚在一起形成的总体称之为集合(Aggregate), 集合对象是能够包容一组对 象的容器对象。不同的集合其内部元素的聚合结构可能不同,而迭代器模式屏蔽了内部元素获 取细节,为外部提供一致的元素访问行为,解耦了元素迭代与集合对象间的耦合,并且通过提 供不同的迭代器,可以为同个集合对象提供不同顺序的元素访问行为,扩展了集合对象元素迭 代功能,符合开闭原则。迭代器模式适用于以下场景:

1、 访问一个集合对象的内容而无需暴露它的内部表示;

2、 为遍历不同的集合结构提供一个统一的访问接口。

首先来看下迭代器模式的通用UML类图:

在这里插入图片描述

从 UML类图中,我们可以看到,迭代器模式主要包含三种角色:

抽象迭代器( Iterator) : 抽象迭代器负责定义访问和遍历元素的接口 ;

具体迭代器( Concreteiterator) :提供具体的元素遍历行为;

抽象容器(Aggregate ) : 负责定义提供具体迭代器的接口 ;

具体容器(ConcreteAggregate ) :创建具体迭代器。

1.5 手写字定义的送代器

总体来说,迭代器模式还是非常简单的。我们还是以课程为例,下面我们自己创建一个课程的集合,集合中的每一个元素就是课程对象,然后自己手写一个迭代器,将每一个课程对象的信息读出来。

首先创建集合元素课程Course类 :

 public class Course {
     private String name;
 
     public Course(String name) {
         this.name = name;
     }
 
     public String getName() {
         return name;
     }
 }

然后创建自定义迭代器Iterator接口 :

 public interface Iterator<E> {
     E next();
     boolean hasNext();
 }

然后创建自定义的课程的集合ICourseAggregate接口 :

 public interface ICourseAggregate {
     void add(Course course);
     void remove(Course course);
     Iterator<Course> iterator();
 }

然后,分别实现迭代器接口和集合接口,创建Iteratorlmpl实现类:

 public class IteratorImpl<E> implements Iterator<E> {
     private List<E> list;
     private int cursor;
     private E element;
 
     public IteratorImpl(List<E> list) {
         this.list = list;
     }
 
     public E next() {
         System.out.print("当前位置 " + cursor + " : ");
         element = list.get(cursor);
         cursor ++;
         return element;
     }
 
     public boolean hasNext() {
         if(cursor > list.size() - 1){
             return false;
         }
         return true;
     }
 }

创建课程集合CourseAggregatelmpI 实现类:

 public class CourseAggregateImpl implements ICourseAggregate {
     private List courseList;
 
     public CourseAggregateImpl() {
         this.courseList = new ArrayList();
     }
 
     public void add(Course course) {
         courseList.add(course);
     }
 
     public void remove(Course course) {
         courseList.remove(course);
     }
 
     public Iterator<Course> iterator() {
         return new IteratorImpl<Course>(courseList);
     }
 }

然后,编写客户端代码:

 public class Test {
     public static void main(String[] args) {
         Course java = new Course("Java架构");
         Course javaBase = new Course("Java基础");
         Course design = new Course("设计模式");
         Course ai = new Course("人工智能");
 
         ICourseAggregate aggregate = new CourseAggregateImpl();
         aggregate.add(java);
         aggregate.add(javaBase);
         aggregate.add(design);
         aggregate.add(ai);
 
         System.out.println("===========课程列表==========");
         printCourse(aggregate);
 
         aggregate.remove(ai);
 
         System.out.println("===========删除操作之后的课程列表==========");
         printCourse(aggregate);
     }
 
     private static void printCourse(ICourseAggregate aggregate) {
         Iterator<Course> i = aggregate.iterator();
         while (i.hasNext()){
             Course course = i.next();
             System.out.println("《" + course.getName()  + "》");
         }
     }
 }

在这里插入图片描述

运行结果如下:

在这里插入图片描述

看到这里,小伙伴们肯定会有一种似曾相识的感觉,让人不禁想起我们每天都在用的JDK 自带的结合迭代器。下面我们就来看看源码中是如何运用迭代器的。

1.6 迭代器模式在源码中的体现

先来看JDK中大家非常熟悉的Iterator源码 :

 public interface Iterator<E> {
     boolean hasNext();
 
     E next();
 
     default void remove() {
         throw new UnsupportedOperationException("remove");
     }
 
     default void forEachRemaining(Consumer<? super E> action) {
         Objects.requireNonNull(action);
         while (hasNext())
             action.accept(next());
     }
 }

从上面代码中,我们看到两个主要的方法定义hasNext()和 next()方 法 ,和我们自己写的完 全一致。

另外,从上面的代码中,我们看到removeO方法实现似曾相识。其实是在组合模式中我们 见到过。迭代器模式和组合模式,两者似乎存在一定的相似性。组合模式解决的是统一树形结 构各层次访问接口,迭代器模式解决的是统一各集合对象元素遍历接口。虽然他们的适配场景 不同,但核心理念是相通的。

下面接看来看Iterator的实现类,其实在我们常用的ArrayList中有一个内部实现类Itr ,它 就实现了 Iterator接口 :

 public class ArrayList<E> extends AbstractList<E>
         implements List<E>, RandomAccess, Cloneable, java.io.Serializable
 {
     private class Itr implements Iterator<E> {
         int cursor;       // index of next element to return
         int lastRet = -1; // index of last element returned; -1 if no such
         int expectedModCount = modCount;
 
         public boolean hasNext() {
             return cursor != size;
         }
 
         @SuppressWarnings("unchecked")
         public E next() {
             checkForComodification();
             int i = cursor;
             if (i >= size)
                 throw new NoSuchElementException();
             Object[] elementData = ArrayList.this.elementData;
             if (i >= elementData.length)
                 throw new ConcurrentModificationException();
             cursor = i + 1;
             return (E) elementData[lastRet = i];
         }
 
         ...
     }
 }

其中hasNextO方法和next()方法实现也非常简单,我们继续往下看在ArrayList内部还有 几个迭代器对Itr进行了进一步扩展,首先看Listltr :

private class ListItr extends Itr implements ListIterator<E> {
    ListItr(int index) {
        super();
        cursor = index;
    }

    public boolean hasPrevious() {
        return cursor != 0;
    }

    public int nextIndex() {
        return cursor;
    }

    public int previousIndex() {
        return cursor - 1;
    }

    ...
}

它增加了 hasPreviousQ方法是否还有上一个等这样的判断。另外还有SubList对子集合的迭代处理。

当然,迭代器模式在MyBatis中也是必不可少的,来看一个Defaultcursor类 :

public class DefaultCursor<T> implements Cursor<T> {
	...
    private final CursorIterator cursorIterator = new CursorIterator();
}

首先它实现了 Cursor接 口 ,而且定义了一个成员变量cursoriterator , 我继续查看 Cursoriterator的源代码发现, 它 是 Defaultcursor的一个内部类,并且实现了 JDK中的 Iterater 接口。

1.7 迭代器模式的优缺点

优点:

1、 多态迭代:为不同的聚合结构提供一致的遍历接口,即一个迭代接口可以访问不同的集合对 象 ;

2、 简化集合对象接口 :迭代器模式将集合对象本身应该提供的元素迭代接口抽取到了迭代器中 ,使集合对象无须关心具体迭代行为;

3、 元素迭代功能多样化:每个集合对象都可以提供一个或多个不同的迭代器,使的同种元素聚合结构可以有不同的迭代行为;

4、解耦迭代与集合:迭代器模式 封装了具体的迭代算法,迭代算法的变化,不会影响到集合对象的架构。

缺点:

1、对于比较简单的遍历(像数组或者有序列表) ,使用迭代器方式遍历较为繁琐。 在日常开发当中,我们几乎不会自己写迭代器。除非我们需要定制一个自己实现的数据结构 对应的迭代器,否则,开源框架提供给我们的API完全够用。

2 命令模式

2.1 定义

命令模式(Command Pattern )是对命令的封装,每一个命令都是一个操作:请求的一方发出请求要求执行一个操作;接收的一方收到请求,并执行操作。命令模式解耦了请求方和接收方,请求方只需请求执行命令,不用关心命令是怎样被接收,怎样被操作以及是否被执行…等。 命令模式属于行为型模式。

原 文 : Encapsulate a request as an object, there by letting you parameterize clients with different requests, queue or log requests,and support undoable operations.
解释:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。

在软件系统中,行为请求者与行为实现者通常是一种紧耦合关系,因为这样的实现简单明了。 但紧耦合关系缺乏扩展性,在某些场合中,当需要为行为进行记录,撤销或重做等处理时,只 能修改源码。而命令模式通过为请求与实现间引入一个抽象命令接口,解耦了请求与实现,并 且中间件是抽象的,它可以有不同的子类实现,因此其具备扩展性。所以,命令模式的本质是 解耦命令请求与处理。

2.2 命令模式的应用场景

当系统的某项操作具备命令语义时,且命令实现不稳定(变化),那么可以通过命令模式解耦请求与实现,利用抽象命令接口使请求方代码架构稳定,封装接收方具体命令实现细节。接收方与抽象命令接口呈现弱耦合(内部方法无需一致),具备良好的扩展性。

命令模式适用于 以下应用场景:

1、现实语义中具备"命令"的操作(如命令菜单,shell命令…);

2、请求调用者和请求的接收者需要解耦,使得调用者和接收者不直接交互;

3、需要抽象出等待执行的行为,比如撤销(Undo)操作和恢复(Redo)等操作;

4、需要支持命令宏(即命令组合操作)。

首先看下命令模式的通用UML类图:

在这里插入图片描述

从 UML类图中,我们可以看到,命令模式 主要包含四种角色:

接收者角色(Receiver):该类负责具体实施或执行一个请求;

命令角色(Command ):定义需要执行的所有命令行为;

具体命令角色(Concrete Command )该类内部维护一个接收者(Receiver ),在其execute() 方法中调用Receiver的相关方法;

请求者角色(Invoker):接收客户端的命令,并执行命令。

从命令模式的UML类图中,其实可以很清晰地看出:Command的出现就是作为Receiver 和 Invoker的中间件,解耦了彼此。而之所以引入Command中间件,我觉得是以下两方面原因 :

解耦请求与实现:即解耦了 Invoker和 Receiver , 因为在UML类图中,Invoker是一个具 体的实现,等待接收客户端传入命令(即 Invoker与客户端耦合),Invoker处于业务逻辑区域, 应当是一个稳定的结构。而 Receiver是属于业务功能模块 是经常变动的 如果没有Command z 则 Invoker紧耦合Receiver , 一个稳定的结构依赖了一个不稳定的结构,就会导致整个结构都不稳定了。这也就是Command引入的原因:不仅仅是解耦请求与实现,同时稳定( Invoker) 依赖稳 定 (Command ),结构还是稳定的。

扩展性增强:扩展性体现在两个方面:

1、 Receiver属于底层细节,可以通过更换不同的Receiver达到不同的细节实现;

2、 Command接口本身就是抽象的,本身就具备扩展性;而且由于命令对象本身就具备抽 象 ,如果结合装饰器模式,功能扩展简直如鱼得水。

注:在一个系统中,不同的命令对应不同的请求,也就是说无法把请求抽象化,因此命令模式中的Receiver是具体实 现;但是如果在某一个模块中,可以对Receiver进行抽象,其实这就变相使用到了桥接模式(Command类具备两个变 化的维度:Command和 Receiver), 这样子的扩展性会更加优秀。

举个生活中的例子,相信80后的小伙伴应该都经历过普及黑白电视机的那个年代。黑白电 视机要换台那简直不容易,需要人跑上前去用力掰动电视机上那个切换频道的旋钮,一 顿 〃啪 啪啪〃折腾下来才能完成一次换台。如今时代好了,我们只需躺沙发上按一下遥控器就完成了 换台。这就是用到了命令模式,将换台命令和换台处理进行了分离。

另外,就是餐厅的点菜单,一般是后厨先把所有的原材料组合配置好了,客户用餐前只需要 点菜即可,将需求和处理进行了解耦。

2.3 命令模式在业务场景中的应用

假如我们自己开发一个播放器,播放器有播放功能、有拖动进度条功能、停止播放功能、暂 停功能,我们自己去操作播放器的时候并不是直接调用播放器的方法,而是通过一个控制条去传达指令给播放器内核,那么具体传达什么指令,会被封装为一个一个的按钮。那么每个按钮 的就相当于是对一条命令的封装。用控制条实现了用户发送指令与播放器内核接收指令的解耦。

下面来看代码,首先创建播放器内核GPlayer类:

public class GPlayer {
    public void play(){
        System.out.println("正常播放");
    }

    public void speed(){
        System.out.println("拖动进度条");
    }

    public void stop(){
        System.out.println("停止播放");
    }

    public void pause(){
        System.out.println("暂停播放");
    }
}

创建命令接口 IAction类:

public interface IAction {
    void execute();
}

然后分别创建操作播放器可以接受的指令,播放指令PlayAction类 :

public class PlayAction implements IAction {
    private GPlayer gplayer;

    public PlayAction(GPlayer gplayer) {
        this.gplayer = gplayer;
    }

    public void execute() {
        gplayer.play();
    }
}

暂停指令PauseAction类 :

public class PauseAction implements IAction {
    private GPlayer gplayer;

    public PauseAction(GPlayer gplayer) {
        this.gplayer = gplayer;
    }

    public void execute() {
        gplayer.pause();
    }
}

拖动进度条指令SpeedAction类 :

public class SpeedAction implements IAction {
    private GPlayer gplayer;

    public SpeedAction(GPlayer gplayer) {
        this.gplayer = gplayer;
    }

    public void execute() {
        gplayer.speed();
    }
}

停止播放指令StopAction类 :

public class StopAction implements IAction {
    private GPlayer gplayer;

    public StopAction(GPlayer gplayer) {
        this.gplayer = gplayer;
    }

    public void execute() {
        gplayer.stop();
    }
}

最后 ,创建控制条Controller类 :

public class Controller {
    private List<IAction> actions = new ArrayList<IAction>();

    public void addAction(IAction action){
        actions.add(action);
    }

    public void execute(IAction action){
        action.execute();
    }

    public void executes(){
        for (IAction action:actions) {
            action.execute();
        }
        actions.clear();
    }
}

从上面代码来看,控制条可以执行单条命令,也可以批量执行多条命令。下面来看客户端测试代码:

public class Test {
    public static void main(String[] args) {
        GPlayer player = new GPlayer();
        Controller controller = new Controller();
        controller.execute(new PlayAction(player));

        controller.addAction(new PauseAction(player));
        controller.addAction(new PlayAction(player));
        controller.addAction(new StopAction(player));
        controller.addAction(new SpeedAction(player));
        controller.executes();
    }
}

运行效果如下:

正常播放
暂停播放
正常播放
停止播放
拖动进度条

由于控制条已经与播放器内核解耦了,以后如果想扩展新命令,只需增加命令即可,控制条 的结构无需改动。

2.4 命令模式在源码中的体现

首先来看JDK中的Runnable接口 ,实际上Runnable就相当于是命令的抽象,只要是实现 了 Runnable接口的类都被认为是一个线程。

public interface Runnable { 
    public abstract void run();
}

实际上调用线程的start()方法之后,就有资格去抢CPU资源,而不需要我们自己编写获得 CPU资源的逻辑。而线程抢到CPU资源后,就会执行run()方法中的内容,用 Runnable接口 把用户请求和CPU执行进行了解耦。

然 后 ,再看一个大家非常孰悉的junit.framework.Test接口 :

package junit.framework; 
public interface Test { 
    public abstract int countTestCases(); 
    public abstract void run(TestResult result);
}

Test接口中有两个方法,第一个是countTestCases()方法用来统计当前需要执行的测试用例 总数。第二个是run()方法就是用来执行具体的测试逻辑,其参数TestResult是用来返回测试结果的。 实际上我们在平时编写测试用例的时候,只需要实现Test接口即便认为就是一个测试用例,那 么在执行的时候就会自动识别。实际上我们平时通常做法都是继承TestCase类 ,我们不妨来看 —下 TestCase的源码:

public abstract class TestCase extends Assert implements Test { 
    ...
    public void run(TestResult result) { 
        result.run(this);
	}
    ...
}

实际上TestCase类它也实现了 Test接口。我们继承TestCase类 ,相当于也实现了 Test 接口,自然也就会被扫描成为一个测试用例。

2.5 命令模式的优缺点

优点:

1、通过引入中间件(抽象接口),解耦了命令请求与实现;

2、 扩展性良好,可以很容易地增加新命令;

3、 支持组合命令,支持命令队列;

4、可以在现有命令的基础上,增加额外功能(比如日志记录…,结合装饰器模式更酸爽)。

缺点:

1、 具体命令类可能过多;

2、 命令模式的结果其实就是接收方的执行结果,但是为了以命令的形式进行架构,解耦请求与 实现,引入了额外类型结构(引入了请求方与抽象命令接口),增加了理解上的困难(不过这 也是设计模式带来的一个通病,抽象必然会引入额外类型;抽象肯定比紧密难理解)。

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

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

相关文章

UVa 211 The Domino Effect 多米诺效应 暴力搜索

题目链接&#xff1a;UVa 211 The Domino Effect 题目描述&#xff1a; 一张多米诺骨牌拥有两个数值&#xff0c;一共有二十八张不同的多米诺骨牌&#xff0c;这二十八张多米诺骨牌的点数如下图所示&#xff1a; 上图的BoneBoneBone代表编号&#xff0c;而PipsPipsPips代表两个…

Springboot扩展点系列之终结篇:Bean的生命周期

前言关于Springboot扩展点系列已经输出了13篇文章&#xff0c;分别梳理出了各个扩展点的功能特性、实现方式和工作原理&#xff0c;为什么要花这么多时间来梳理这些内容&#xff1f;根本原因就是这篇文章&#xff1a;Spring bean的生命周期。你了解Spring bean生命周期&#xf…

前端最全面试题整理

前端基础 一、 HTTP/HTML/浏览器 1、说一下 http 和 https https 的 SSL 加密是在传输层实现的。 (1) http 和 https 的基本概念 http: 超文本传输协议&#xff0c;是互联网上应用最为广泛的一种网络协议&#xff0c;是一个客户端和服务器端请求和应答的标准&#xff08;T…

操作SSH无密登录配置

例如小编有三台服务器需要相互访问&#xff0c;就需要配置三台&#xff0c;这三台分别是hadoop102,hadoop103 , hadoop1041.打开三个服务器&#xff0c;分别生成hadoop102&#xff0c;hadoop103 , hadoop104的公钥和私钥输入命令&#xff0c;然后一直回车&#xff0c;这时候什么…

狂神聊Redis复习笔记二

目录事务监控&#xff01; Watch &#xff08;面试常问&#xff01;&#xff09;悲观锁&#xff1a;乐观锁&#xff1a;Redis测监视测试Redis.conf详解Redis持久化RDB&#xff08;Redis DataBase&#xff09;AOF&#xff08;Append Only File&#xff09;Redis发布订阅Redis主从…

Docker资源隔离(namespace,cgroups)

一、概述 Docker容器的本质是宿主机上的一个进程。Docker通过namespace实现了资源隔离&#xff0c;通过cgroups实现了资源限制&#xff0c;通过写时复制机制&#xff08;copy-on-write&#xff09;实现了高效的文件操作。 二、Linux内核的namespace机制 namespace 机制提供一种…

jfr引起的一次jvm异常记录

业务生产启动时&#xff0c;20个节点有1-2个节点因为jvm问题出现启动失败&#xff0c;k8s自动重启后正常。在测试环境2个节点下偶现 排查思路&#xff1a; 先拿到hs_err_pid的jvm错误文件找到当前线程和内部错误信息 hs_err_pid 文件分析 当前线程&#xff1a;lettuce的线程…

使用Robot Framework实现多平台自动化测试

目录 前言 1、设计目标 2、架构设计 3、平台实现 4、平台的创新点 5、平台的实施效果 6、总结 重点&#xff1a;配套学习资料和视频教学 前言 基于Robot Framework、Jenkins、Appium、Selenium、Requests、AutoIt等开源框架和技术&#xff0c;成功打造了通用自动化测试…

各数据库数据类型的介绍和匹配

各数据库数据类型的介绍和匹配1. Oracle的数据类型2. Mysql的数据类型3. Sql server的数据类型4. 类型匹配5. Awakening1. Oracle的数据类型 数据类型介绍 VARCHAR2 :可变长度的字符串 最大长度4000 bytes 可做索引的最大长度749&#xff1b; NCHAR :根据字符集而定的固定长度字…

微服务架构的演变

文章目录1.1 系统架构的演变过程1.1.1 单体应用架构1.1.2 垂直应用架构1.1.3 分布式架构1.1.4 SOA架构1.1.5 微服务架构1.2 微服务架构设计原则1.2.1 AKF拆分原则1.2.1.1 X轴扩展&#xff08;水平复制&#xff09;1.2.1.2 Y轴扩展&#xff08;模块拆分&#xff09;1.2.1.3 Z轴扩…

【SSM】Spring对IoC的实现方式DI详讲

控制反转的一种实现方式——依赖注入一、IoC 控制反转&#xff08;Overview&#xff09;依赖注入&#xff08;DI&#xff09;- Overview利用 IoC&#xff08;控制反转&#xff09;这种思想有什么好处呢&#xff1f;二、依赖注入的方式setter 方式&#xff08;xml配置中的proper…

Java JSR规范列表

Java JSR规范列表目录概述需求&#xff1a;设计思路实现思路分析1.JSR2.JSR方法3.web service4.Webservice:5.数据处理器拓展实现参考资料和推荐阅读Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,m…

JavaWeb-连接数据库实现用户登录、注册、修改密码(全代码)

上一篇博客中我通过使用HttpServlet完成一个假登录&#xff0c;这篇博客我将通过JDBC连接数据库&#xff0c;使其实现简单的用户登录、注册以及更改密码一、MySQL:MySQL部分代码&#xff1a;-- ---------------------------- -- Table structure for users -- ----------------…

WSL(ubuntu2204)使用xfce4桌面打不开语言支持及配置WSL服务自启

语言支持报错 在图形桌面或命令行打开语言支持报错&#xff1a;dbus.exceptions.DBusException: org.freedesktop.DBus.Error.FileNotFound: Failed to connect to socket /run/dbus/system_bus_socket: No such file or directory itboonelocalhost:/$ sudo /usr/bin/gnome-…

【SCL】1200案例:天塔之光数码管显示液体混合水塔水位

使用scl编写天塔之光&数码管显示&液体混合&水塔水位 文章目录 目录 文章目录 前言 一、案例1&#xff1a;天塔之光 1.控制要求 2.编写程序 3.效果 二、案例2&#xff1a;液体混合 1.控制要求 2.编写程序 三、案例3&#xff1a;数码管显示 1.控制要求 2.编写程序 3…

转载:项目分析信息方法论

转载一篇最近看到的项目分析信息法&#xff1a;如何快速分析项目和如何详细分析项目。 一、如何快速分析项目&#xff1f; 可以从6个点进行分析&#xff0c;分别是&#xff1a;「流量效率&#xff0c;销转效率&#xff0c;交付效率&#xff0c;客单价&#xff0c;毛利率&…

【程序化天空盒】过程记录02:云扰动 边缘光 消散效果

写在前面 写在前面唉&#xff0c;最近筋疲力竭&#xff0c;课题组的东西一堆没做&#xff0c;才刚刚开始带着思考准备练习作品&#xff0c;从去年5月份开始到现在真得学了快一年了&#xff0c;转行学其他的真的好累&#xff0c;&#xff0c;不过还是加油&#xff01; 下面是做…

zlib压缩原理

数据压缩的本质 去除数据中的冗余信息&#xff0c;对于ABABABABABABAB字样的字符串&#xff0c;AB出现了7次&#xff0c;占用14个字节&#xff0c;如果将该字符串编码为7AB&#xff0c;只占用3个字节。 为什么需要对数据压缩 数据需要存储或者传输&#xff0c;为了节省磁盘空…

ONNXRUNTUIME实例分割网络说明

ONNXRUNTUIME c使用&#xff08;分割网络&#xff09;与相关资料&#xff08;暂记&#xff09; initiate a env with an id name(使用id名称启动env) create session (创建会话 ) onnxenv -> sessioninputname [“x”] ,outputname [“t”]inputnodedim [[1,1,192,192…

Linux单一服务管理systemctl

基本上systemd这个启动服务机制只有systemctl命令来处理&#xff0c;所以全部的操作都需要使用systemctl systemctl管理单一服务 一般来说服务的启动有两个阶段&#xff0c;一个是开机是否启动&#xff0c;以及现在是否启动 systemctl【command】【unit】 command主要有&…