设计模式—观察者模式(Observer)

news2024/11/24 10:46:20

目录

思维导图

一、什么是观察者模式?

二、有什么优点吗?

三、有什么缺点吗?

四、什么时候使用观察者模式?

五、代码展示

①、双向耦合的代码

②、解耦实践一

③、解耦实践二

④、观察者模式

六、这个模式涉及到了哪些知识点?


思维导图

一、什么是观察者模式?

又叫发布-订阅(publish/Subscrib)模式。定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己。

观察者类似于卧底这样一个角色。在上班的时候,难免会有人趁老板不在的时候做一些工作之外的事情,例如炒炒股,看看NBA之类的,那如何应对老板突然回来这件事情呢?我们就需要卧底这样一个人给我们放风,老板一回来就通知这些人“老板回来了”。这其实就是一种观察者模式

Subject类:它把所有对观察者对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。

Observer类:抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。

ConcreteSubject类:具体主题,将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。

ConcreteObserver类:具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。

二、有什么优点吗?

观察者模式所做的工作其实就是在解除耦合,让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。满足依赖倒转原则

三、有什么缺点吗?

如果观察者很多的话,一个一个的通知会影响效率

四、什么时候使用观察者模式?

当一个对象的改变需要同时改变其他对象的时候。

而且它不知道具体有多少对象有待改变时,应该考虑使用观察者模式。


五、代码展示

场景:小菜公司的同事在老板外出的期间偷偷地通过网页看股票行情,但是一不小心老板就回来了,让老板看到工作的时候做这些总是不太好的,如果避免这样的情况呢?就需要前台秘书这样的一个卧底,老板一回来了,前台秘书童子喆就拨了个电话告诉同事。但有一次因为老板回来直接就找童子喆做一些其他事情,童子喆就没来得及打电话,导致同事就没被通知到“老板回来了”

①、双向耦合的代码

	//前台秘书类
    class Secretary
    {
        //同事列表
        private IList<StockObserver> observers = new List<StockObserver>();    //IList接口,List泛型,集合数据类型是StockObserver
        private string action;                  //成员变量

        //构造方法
        public Secretary() { }

        //增加
        public void Attach(StockObserver observer)
        {
            observers.Add(observer);           //向集合里面添加同事
        }

        //通知
        public void Notify()
        {
            foreach (StockObserver o in observers)    //foreach遍历集合:数据类型  变量名 in 遍历的集合
                o.Update();                           //一个一个的给同事通知
        }

        //前台状态
        public string SecretaryAction
        {
            get { return action; }                //get访问器
            set { action = value; }             //set访问器
        }
    }
 
    //看股票同事类
    class StockObserver
    {
        private string name;            //字符串成员变量
        private Secretary sub;          //前台秘书类类型成员变量
        public StockObserver(string name, Secretary sub)    //构造方法
        {
            this.name = name;           //赋值
            this.sub = sub;
        }

        public void Update()    //通知
        {
            Console.WriteLine("{0}{1}关闭股票行情,继续工作!", sub.SecretaryAction, name); //要通知的语句
        }
    } 

客户端代码:

//前台小姐童子喆
Secretary tongzizhe = new Secretary();

//看股票的同事
StockObserver tongshi1 = new StockObserver("魏关姹", tongzizhe);
StockObserver tongshi2 = new StockObserver("易管查", tongzizhe);

//前台记下了两位同事
tongzizhe.Attach(tongshi1);
tongzizhe.Attach(tongshi2);

//发现老板回来
tongzizhe.SecretaryAction = "老板回来了";

//通知两个同事
tongzizhe.Notify();

Console.ReadKey ();

问题:前台类和看股票者类之间互相耦合。

②、解耦实践一

抽象观察类


abstract class Observer
{
    protected string name;
    protected Secretary sub;
    public Observer(string name, Secretary sub)     //有参的构造方法
    {
        this.name = name;
        this.sub = sub;
    }

    //通知
    public abstract void Update();
}

看股票的同事

class StockObserver : Observer
{
    //继承父类的构造方法
    public StockObserver(string name, Secretary sub) : base(name, sub) { }

    public override void Update()
    {
    	Console.WriteLine("{0}{1} 关闭股票行情,继续工作!", sub.SecretaryAction, name);
    }
}

看NBA的同事

class NBAObserver : Observer
{
    public NBAObserver(string name, Secretary sub) : base(name, sub) { }

    public override void Update()
    {
    	Console.WriteLine("{0}{1} 关闭NBA直播,继续工作!", sub.SecretaryAction, name);
    }
}

   前台秘书类

	class Secretary
    {
        private IList<Observer> observers = new List<Observer>();   //集合
        private string action;                                      //成员变量
        
        //增加
        public void Attach(Observer observer)    //针对抽象编程,减少了与具体类的耦合
        {
            observers.Add(observer);
        }

        //减少
        public void Detach(Observer observer)    //针对抽象编程,减少了与具体类的耦合
        {
            observers.Remove(observer);
        }

        //通知
        public void Notify()
        {
            foreach (Observer o in observers)
                o.Update();
        }

        //前台状态
        public string SecretaryAction     //属性
        {
            get { return action; }
            set { action = value; }
        }
    } 

客户端代码

 //前台小姐童子喆
Secretary tongzizhe = new Secretary();

//看股票的同事
StockObserver tongshi1 = new StockObserver("魏关姹", tongzizhe);
StockObserver tongshi2 = new StockObserver("易管查", tongzizhe);

//前台记下了两位同事
tongzizhe.Attach(tongshi1);
tongzizhe.Attach(tongshi2);

//发现老板回来
tongzizhe.SecretaryAction = "老板回来了";

//通知两个同事
tongzizhe.Notify();

Console.ReadKey ();

③、解耦实践二

    通知者接口

	interface Subject
    {
        void Attach(Observer observer);     //增加
        void Detach(Observer observer);     //减少
        void Notify();                      //通知
        string SubjectState                 //前台状态
        {
            get;
            set;
        }
    }

    老板

	class Boss : Subject
    {
        //同事列表
        private IList<Observer> observers = new List<Observer>();
        private string action;

        //增加
        public void Attach(Observer observer)
        {
            observers.Add(observer);
        }

        //减少
        public void Detach(Observer observer)
        {
            observers.Remove(observer);
        }

        //通知
        public void Notify()
        {
            foreach (Observer o in observers)
                o.Update();
        }

        //前台状态
        public string SubjectState
        {
            get { return action; }
            set { action = value; }
        }
    }

前台秘书类

	class Secretary
    {
        //同事列表
        private IList<Observer> observers = new List<Observer>();
        private string action;

        //增加
        public void Attach(Observer observer)
        {
            observers.Add(observer);
        }

        //减少
        public void Detach(Observer observer)
        {
            observers.Remove(observer);
        }

        //通知
        public void Notify()
        {
            foreach (Observer o in observers)
                o.Update();
        }

        //前台状态
        public string SubjectState
        {
            get { return action; }
            set { action = value; }
        }
    }

抽象观察者

	abstract class Observer
    {
        protected string name;          //成员变量,姓名
        protected Subject sub;          //成员变量,通知者

        public Observer(string name, Subject sub)   //构造方法
        {
            this.name = name;                   //赋值
            this.sub = sub;
        }
        public abstract void Update();
    }

看股票的同事

	class StockObserver : Observer
    {
        public StockObserver(string name, Subject sub) : base(name, sub) { }

        public override void Update()
        {
            Console.WriteLine("{0}{1} 关闭股票行情,继续工作!", sub.SubjectState, name);
        }
    }
    #endregion

客户端代码

//老板胡汉三
Boss huhansan = new Boss();

//看股票的同事
StockObserver tongshi1 = new StockObserver("魏关姹", huhansan);
//看NBA的同事
StockObserver tongshi2 = new StockObserver("易管查", huhansan);

huhansan.Attach(tongshi1);
huhansan.Attach(tongshi2);

huhansan.Detach(tongshi1);            //魏关姹其实是没有被老板通知到,所以减去

//老板回来
huhansan.SubjectState = "我胡汉三回来了";
//发出通知
huhansan.Notify();

Console.ReadKey();

④、观察者模式

Subject:抽象通知者/主题,一般用一个抽象类或者一个接口实现.它把所有对观察者对象的引用保存在一个聚集里,每个主题都可以由任何数量的观察者.抽象主题提供一个接口,可以增加和删除观察者对象

	abstract class Subject
    {
        private IList<Observer> observers = new List<Observer>();

        //增加观察者
        public void Attach(Observer observer)
        {
            observers.Add(observer);
        }

        //删除观察者
        public void Detach(Observer observer)
        {
            observers.Remove(observer);
        }

        //通知
        public void Notify()
        {
            foreach (Observer o in observers)
                o.Update();
        }
    }

Observer类:抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己.这个接口叫做更新接口.抽象观察者一般用一个抽象类或者一个接口实现.更新接口通常包含一个Update()方法,这个方法叫做更新方法.

abstract class Observer
{
	public abstract void Update();
}

ConcreteSubject:具体主题/具体通知者,将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知.具体主题角色同一个具体子类实现.

class ConcreteSubject : Subject
{
    private string subjectState;
    //具体被观察者状态
    public string SubjectState
    {
        get { return subjectState; }
        set { subjectState = value; }
    }
}

// ConcreteObserver:具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调.具体观察者角色可以保存一个指向具体主题对象的引用.具体观察者角色通常用一个具体子类实现.

	class ConcreteObserver : Observer
    {
        //成员变量
        private string name;
        private string observerState;
        private ConcreteSubject subject;

        //构造方法
        public ConcreteObserver(ConcreteSubject subject,string name)
        {
            this.subject = subject;    //赋值
            this.name = name;
        }

        //重写抽象类Update方法
        public override void Update()
        {
            observerState = subject.SubjectState;     //赋值
            Console.WriteLine("观察者{0}的新状态是{1}", name, observerState);  //控制台输出结果
        }

        //具体观察者状态
        public ConcreteSubject Subject 
        {
            get { return subject; }
            set { subject = value; }
        }
    }

观察者模式的动机是将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就需要维护相关对象间的一致性.我们不希望为了维持一致性而使各类紧密耦合,这样会给维护\扩展和重用都带来不便.


六、这个模式涉及到了哪些知识点?

①、一个类里面有哪些东西?

②、字段和属性

字段

是什么?与类相关的变量;

干什么的?用来保存数据

属性

是什么?一个方法或一对方法

什么作用?具有两个方法,get、set访问器,读、写值

  • get访问器:返回与声明的属性相同的数据类型,表示的意思是调用时可以得到内部字段的值或引用;
  • set访问器:没有显式设置参数,但它有一个隐式参数,用关键字value表示,它的作用是调用属性时可以给内部的字段或引用赋值

显示和隐式字段初始化

字段的初始值必须是编译时可确定的。

如果没有初始化语句,字段的值会被编译器设为默认值,默认值由字段的类型决定。每种值类型的默认值都是0,bool型是false,引用类型默认为null。

class MyClass、
{
	int F1;	         //初始化为0     -值类型
    string F2;		 //初始化为null  -引用类型
    
    int F3 =25;		 //初始化为25
    string F4="abcd";  //初始化为“abcd”
    
    //声明多个字段,东逗号分隔
    int F1,F3 =25;
    string F2,F4="abcd";
}

③、访问修饰符

什么作用?指定程序的其他部分如何访问成员

有哪些?

  • private:私有的,只在类的内部可访问,成员默认是这个
  • public:公有的,对任何类可访问
  • protected:受保护的,只允许该类的派生类访问
  • internal:内部的,同一项目所有类可访问

④、构造方法

目的:对类进行初始化

特点:与类同名,无返回值、无void、在new时候调用

所有的类都有构造方法,不带参数的构造方法称为“默认构造方法”,如果你不编码则系统默认生成空的构造方法。若定义了新的构造方法,那么默认的构造方法就会失效。

⑤、方法重载

目的:在不改变原方法的基础上,新增功能

特点:一同二不同:多个方法名相同、参数类型/个数不同

class Animal
{
    private string name;
    
    //方法重载:方法名相同、数据类型/个数不同
    public Animal(){}          //无参的构造方法
    public Animal(string name) //有参的构造方法
    {
        this.name = name;
    }
}

⑥、foreach循环

连续访问数组中的每一个元素。

//语法
foreach(Type Identifier in ArrayName)
	Statement
    
    
//实例
int[] arr1 = {10,11,12,13};

foreach(int item in arr1)
	Console.WriteLine("Item Value:{0}",item);

⑦、List泛型集合

  • arrayList集合:不知道存什么类型,不知道存多少个
  • List:知道存什么类型,不知道存多少个。就是为了专门处理某种类型,在尖括号中写什么类型,这个集合就变成了什么类型的集合
  • 添加数据、插入数据、索引访问数据都是这个类型的,不用考虑所有的转换问题
static void Main(string[] args)
{
    List<int> list = new List<int>();     //实例化int类型
    //随机的往这个List集合中添加十个数字,不能重复,求和,求最大值,求最小值,求平均值
    Random r = new Random();
    int num = 0;
    while (list.Count !=10)
    {
    	num = r.Next(1, 100);
    	if (!list.Contains (num))
    	{
    		list.Add(num);
    	}
    }
    
    Console.WriteLine("最大值:{0}",list.Max ());
    Console.WriteLine("最小值:{0}",list.Min ());
    Console.WriteLine("和为:{0}",list .Sum ());
    Console.WriteLine("平均值为:{0}",list.Average ());
    Console.ReadKey();

    List<string> listStr = new List<string>();   //实例化string类型
    listStr.Add("哈哈,小杨又变帅了");
}

⑧、数组、ArrayList和List三者之间的对比:

⑨、抽象类

目的:抽取相同代码,实现封装思想

特点:

  • 抽象类不能实例化;
  • 抽象方法是必须被子类重写的方法;
  • 如果类中包含抽象方法,那么类就必须定义为抽象类,不论是否还包含其他一般方法

⑩、重写

将父类实现替换为它自己的实现

虚成员

抽象成员

关键字

virtual

abstract

实现体

有实现体

没有实现体,被分号取代

在派生类中被覆写

可重写,也可不重写,使用override

必须被重写,使用override

⑩①、接口

接口提出了一种契约(或者说规范),让使用接口的程序设计人员必须严格遵守接口提出的约定。接口就可以看做是这种标准,它强制性地要求派生类必须实现接口约定的规范,以保证派生类必须拥有某些特性。

特点:

  • 不能实例化;
  • 不能有构造方法和字段;
  • 不能有修饰符;
  • 接口包含(事件、索引器、方法、属性);
  • 不包含方法的实现;
  • 实现接口的类必须实现接口中的所有方法和属性

⑩②、六大原则:依赖倒转原则

-高层模块不应该依赖底层模块。两个都应该依赖抽象(接口/抽象类)。

-抽象不应该依赖细节(具体类)。细节应该依赖抽象。

我现在要设计一个汽车,我先设计汽车大概的样子,先设计轮胎,根据轮胎在设计底盘,根据底盘在设计车身。现在我想要修改轮胎的尺寸,是不是就需要修改底盘,修改车身,整个全部都得修改?如果我们倒过来看,在设计车身,根据车身在设计底盘,根据底盘在设计轮胎,这样的话如果轮胎的尺寸变了,也不会影响到其他的部分

谁也不要依赖谁,除了约定的接口,大家都可以灵活自如。

针对书上所举的例子:无论主板、CPU、内存、硬盘都是在针对接口设计的。CPU作为主板上一个可移动的、可扩展的部分,在设计主板的时候只需要把接口定义好,内部再复杂我也不让外界知道,而主板只需要预留与CPU阵脚的插槽就可以了。内存、硬盘、显卡都是如此,哪部分坏了直接更换那部分就行了,而不会导致整个主板全部都要换。

 

⑩③、六大关系

  • 依赖:使用关系,一个类的使用需要另一个类的协助

实现:局部变量、构造方法的参数、静态方法的调用

图形:虚线+箭头,指向被拥有者

Animal {
   Public Water Grownup(Water water) 
   {
		return null;
   }
}

  • 关联:拥有关系,一个类需要使用另一个类的属性和方法

实现:成员变量

图形:实线+箭头

class Water {
     public Climate m_Climate;
     public Water(){}
}

class Climate {
     public Climate() {}
}

  • 聚合:整体和部分的关系,部分不能脱离整体而单独存在

实现:成员变量+构造方法的参数赋值

图形:实线+空心菱形,菱形指向整体

class GooseGroup {
    public Goose goose;
    Public GooseGroup(Goose goose) 
    {
		this.goose = goose;
	}
}
class Work
{
    private State current;
    public Work()
    {
        current = new ForenoonState();
    }
}
  • 继承:子类继承父类

实现:子类:父类

图形:实线+空心三角形,箭头指向父类

class Cat:Animal
{

}

  • 实现:类和接口的关系,类实现接口的所有特征

实现:类:接口

图形:虚线+空心三角,箭头指向接口

Class WideGoose:Ifly
{ 

}

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

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

相关文章

12.工作数字钟

效果 源码 <!doctype html> <html><head><meta charset="utf-8"><title>Digital Clock</title><link rel="stylesheet" href="style.css"></head><body><div id="time">…

开源项目的测试和质量保证

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

《Flink学习笔记》——第一章 概念及背景

​ 什么是批处理和流处理&#xff0c;然后由传统数据处理架构为背景引出什么是有状态的流处理&#xff0c;为什么需要流处理&#xff0c;而什么又是有状态的流处理。进而再讲解流处理的发展和演变。而Flink作为新一代的流处理器&#xff0c;它有什么优势&#xff1f;它的相关背…

短视频seo搜索矩阵系统源码----独立应用搭建

前言&#xff1a;抖音账号矩阵系统/抖音seo霸屏系统/抖音矩阵seo系统源码/独立部署,技术团队如何围绕抖音矩阵关键词霸屏来做开发&#xff1f;来做到抖音seo优化达到账号排名效果&#xff0c;关键词起到至关重要的作用&#xff0c;依托于抖音平台的正规权限。 当普通对象属性更…

启动项目时Service有却找不到而报错

解决方法&#xff0c;重启一下Idea&#xff0c;看是否可以&#xff0c;如果不行的话 点击File->setting中 勾上&#xff0c;重启一下。接可以了

PMAC与Modbus主站进行Modbus Tcp通讯

PMAC与Modbus主站进行Modbus Tcp通讯 创建modbus通讯参数 在项目的PMAC Script Language\Global Includes下创建一个名为00_Modbus_Para.pmh的pmh文件。 Modbus[0].Config.ServerPort 0 Modbus[0].Config.ConnectTimeOut 6000 Modbus[0].Config.SendRecvTimeOut 0 Modbu…

基于django框架的学生选课系统jsp学校教务信息java源代码Mysql

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 基于django框架的学生选课系统 系统有2权限&#xff…

好用的电容笔有哪些推荐?适合开学季电容笔推荐

至于是用苹果原装的电容笔还是平替式电容笔&#xff0c;那就要看你自己的使用需求的了&#xff0c;例如常用于绘画上的话&#xff0c;就用苹果笔&#xff1b;如果你每天花在书写上的时间比花在画图上的时间还多&#xff0c;那么你可以考虑买一款便宜一点的平替电容笔。小编整理…

kettle的基础概念入门、下载、安装、部署

1、什么是ETL? 答&#xff1a;ETL&#xff08;Extract-Transform-Load的缩写&#xff0c;即数据抽取、转换、装载的过程&#xff09;&#xff0c;对于企业或行业应用来说&#xff0c;我们经常会遇到各种数据的处理&#xff0c;转换&#xff0c;迁移&#xff0c;所以了解并掌握…

测试人员的核心价值:Bug 要素

什么是Bug&#xff1f;只要不能满足预期的东西都可以称之为Bug。所以&#xff0c;Bug也是广义的Bug&#xff0c;可以分为功能Bug&#xff0c;性能Bug&#xff0c;安全Bug&#xff0c;甚至流程Bug。 对于一个Bug&#xff0c;优秀的测试工程师要能够定位Bug原因&#xff0c;并给…

Golang Gorm 一对多关系 表结构的建立

Belongs To belongs to 会与另一个模型建立了一对一的连接。 这种模型的每一个实例都“属于”另一个模型的一个实例。 例如&#xff0c;应用包含 user 和 company&#xff0c;并且每个 user 能且只能被分配给一个 company。 下面的类型就表示这种关系。 注意&#xff0c;在 U…

MR混合现实石油化工课堂情景实训教学演示

MR&#xff08;混合现实&#xff09;技术是一种结合了虚拟现实&#xff08;VR&#xff09;和增强现实&#xff08;AR&#xff09;优势的新型技术&#xff0c;在教育领域具有广阔的应用前景。在石油化工课堂中&#xff0c;MR混合现实情景实训教学的应用可以大大提高学生的学习效…

Camunda 7.x 系列【32】包含网关

有道无术,术尚可求,有术无道,止于术。 本系列Spring Boot 版本 2.7.9 本系列Camunda 版本 7.19.0 源码地址:https://gitee.com/pearl-organization/camunda-study-demo 1. 概述 Inclusive Gateway包含网关可以看作排他网关和并行网关的结合体。 与排他网关类似,可以定…

ABAP固定点算术

在程序的属性中有一个默认勾选的设置&#xff0c;Fixed point arithmetic 定价例程VOFM所在的函数组是没有勾选的&#xff0c;所以例程中的计算要小心 我们主要测试一下不勾选的情况。我们新建一个程序ytest_cl&#xff0c;注意不要勾选定点运算 DATA:l_kbetr TYPE p LENGTH 1…

活动预告|2023全球纺织碳中和国际峰会,有哪些看点?

会议背景 在全球时尚产业减排的关键期&#xff0c;作为仅次于石化工业的第二大污染产业&#xff0c;时尚行业的绿色生产迫在眉睫。纺织工业每年向大气排放 1.22 至 29.3 亿吨二氧化碳&#xff0c;据估计&#xff0c;纺织品的生命周期&#xff08;包括洗涤&#xff09;占全球温…

罗技M720删除蓝牙连接后,蓝牙搜索列表找不到设备

原因 因误删蓝牙鼠标&#xff08;罗技M720&#xff09;设备&#xff0c;再次添加蓝牙设备时蓝牙列表找不到设备&#xff08;罗技M720&#xff09;。 蓝牙配对 1、确保 M720 已开启 2、按住显示屏下方的切换按钮 3 秒钟&#xff08;所选通道上的 LED 将开始快速闪烁&#xff…

【Java架构-版本控制】-Git进阶

本文摘要 Git作为版本控制工具&#xff0c;使用非常广泛&#xff0c;在此咱们由浅入深&#xff0c;分三篇文章&#xff08;Git基础、Git进阶、Gitlab搭那家&#xff09;来深入学习Git 文章目录 本文摘要1. Git分支管理2. Git分支本质2.1 分支流转流程(只新增文件)2.2 分支流转流…

雪亮工程2.0:雪亮工程二期智能化综合管理AI大数据平台建设方案设计

一、方案背景 雪亮工程主要是针对农村地区治安防控的监控项目&#xff0c;在乡村的主干道、路口、人群聚集地部署高清摄像头&#xff0c;通过三级综治中心和指挥平台&#xff0c;将视频图像信息系统纵向下延至县、乡、村&#xff0c;通过建设各类视频监控点&#xff0c;实现视…

Leetcode 易错题整理(一)5. 7. 11. 15. 33. 34

5. 最长回文子串 给你一个字符串 s&#xff0c;找到 s 中最长的回文子串。 如果字符串的反序与原始字符串相同&#xff0c;则该字符串称为回文字符串。 示例 1&#xff1a; 输入&#xff1a;s "babad" 输出&#xff1a;"bab" 解释&#xff1a;"aba&q…

ssm+vue中国咖啡文化宣传网站源码和论文

ssmvue中国咖啡文化宣传网站源码和论文078 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm 课题背景 随着时代的发展和人们生活理念的进一步改变&#xff0c;咖啡业已经成为了全球经济中发展最迅猛的产业之一。…