当前位置: 首页 > news >正文

设计模式简要汇总

一、面向对象设计原则

  • 开闭原则:一个软件实体(类、模块、函数)应该对扩展开放,对修改关闭。
  • 依赖倒置原则:高层模块不应该依赖底层模块,它们都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。
  • 里氏替换原则:派生类(子类)对象可以在程序中代替其基类(超类)对象。
  • 单一职责原则:一个类或者模块只负责完成一个职责(或者功能)。
  • 接口隔离原则:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。
  • 合成复用原则:尽量使用对象组合,而不是继承来达到复用的目的。
  • 迪米特原则:每个软件单位对其他单位都具有最少知识,而且局限于那些与本单位密切相关的软件单位。

二、创建型模式

2.1 简单工厂模式

简单工厂(Simple Factory)模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,它定义了一个工厂对象决定创建出哪一种产品类的实例。

简单工厂模式的结构图如下。SimpleFactory.CreateProduct()方法根据传入的参数决定具体的实例化对象,且返回的是抽象类Product。这意味着客户端无需关心创建的产品具体是什么类型。

示例代码如下

// 简单工厂
public class SimpleFactory
{public static Product? CreateProduct(string param){Product? product = null;switch (param){case "A":product = new ProductA();break;case "B":product = new ProductB();break;case "C":product = new ProductC();break;}return product;}
}// 抽象产品类
public abstract class Product
{public abstract void Operate();
}
// 具体产品类
public class ProductA:Product
{public override void Operate(){Console.WriteLine("OperationA");}
}
public class ProductB:Product
{public override void Operate(){Console.WriteLine("OperationB");}
}
public class ProductC:Product
{public override void Operate(){Console.WriteLine("OperationC");}
}

客户端调用如下

Product productA = SimpleFactory.CreateProduct("A");  
productA.Operate();  Product productB = SimpleFactory.CreateProduct("B");  
productB.Operate();  Product productC = SimpleFactory.CreateProduct("C");  
productC.Operate();  // 输出结果:  
// OperationA  
// OperationB  
// OperationC

简单工厂模式的优点是工厂类包含了创建产品的逻辑,客户端只需要传入所需的参数即可获得对应的产品。在这个过程中,客户端甚至不需要知道产品具体是什么类型。

但简单工厂模式的缺点是一旦需要增加产品类型,就需要修改工厂类,这并不符合开闭原则。

2.2 工厂方法模式

工厂方法模式(Factory Method),定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

工厂方法模式的结构图如下。与简单工厂模式不同的是,产品的实例化不再由单一的工厂负责,而是一个工厂生产一种具体的产品。客户端需要哪种类型的产品只需要实例化对应的工厂对象即可。

示例代码如下

// 抽象产品类
public abstract class Product { }
// 具体产品类
public class ProductA:Product { }
public class ProductB:Product { }
public class ProductC:Product { }// 抽象工厂类
public abstract class Factory
{public abstract Product CreateProduct();
}
// 具体工厂类
public class FactoryA : Factory
{public override Product CreateProduct(){return new ProductA();}
}
public class FactoryB : Factory
{public override Product CreateProduct(){return new ProductB();}
}
public class FactoryC : Factory
{public override Product CreateProduct(){return new ProductC();}
}

客户端调用方式如下

Factory factory1 = new FactoryA();  
var product1 = factory1.CreateFruit();  Factory factory2 = new FactoryB();  
var product2 = factory2.CreateFruit();  Factory factory3 = new FactoryC();  
var product3 = factory3.CreateFruit();

工厂方法模式继承了简单工厂模式的优点——避免创建者与具体产品之间的紧密联系,又满足了开闭原则——引入新的产品只需要增加工厂类。由于一个工厂类只负责一种产品的创建,所以同样也满足了单一职责原则。

工厂方法的缺点是需要引入许多新的子类,这会让代码更加复杂。

2.3 抽象工厂模式

抽象工厂模式(Abstract Factory),提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

抽象工厂模式的结构图如下。抽象工厂模式与工厂方法模式有些相似,只不过抽象工厂模式中的工厂可以创建“一系列”产品(如工厂A可以生产A1、B1,工厂B可以生产A2、B2),而工厂方法模式中的工厂只负责单一产品的生产。

示例代码如下

// 抽象工厂类
public abstract class Factory
{public abstract ProductA GetProductA();public abstract ProductB GetProductB();
}// 抽象产品类
public abstract class ProductA
{public abstract void ShowA();
}
public abstract class ProductB
{public abstract void ShowB();
}
// 具体产品类
public class ConcreteProductA1 : ProductA
{public override void ShowA(){Console.WriteLine("ProductA1");}
}
public class ConcreteProductA2 : ProductA
{public override void ShowA(){Console.WriteLine("ProductA2");}
}
public class ConcreteProductB1 : ProductB
{public override void ShowB(){Console.WriteLine("ProductB");}
}
public class ConcreteProductB2 : ProductB
{public override void ShowB(){Console.WriteLine("ProductB2");}
}
// 具体工厂类
public class FactoryA : Factory
{// 生产A1public override ProductA GetProductA(){return new ConcreteProductA1();}// 生产B1public override ProductB GetProductB(){return new ConcreteProductB1();}
}
public class FactoryB : Factory
{// 生产A2public override ProductA GetProductA(){return new ConcreteProductA2();}// 生产B2public override ProductB GetProductB(){return new ConcreteProductB2();}
}

客户端调用

// 确定实例化哪个工厂  
// Factory factory = new FactoryA();  
Factory factory = new FactoryB();  var productA = factory.GetProductA();  
productA.ShowA();  var productB = factory.GetProductB();  
productB.ShowB();  // 输出结果:  
// ProductA2  
// ProductB2

抽象工厂模式的好处便是易于交换产品系列,由于具体工厂类在一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易,它只需要改变具体工厂即可使用不同的产品配置。

其次它让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户代码中。

它的缺点是每增加一种产品就需要进行大量改动。可以通过反射+简单工厂模式对其进行改进来解决这一问题。只需要用下面这个工厂代替其他工厂即可

public class FactoryPlus
{// 改变产品系列只需要修改这个字段// private static readonly string ProductType = "1";private static readonly string ProductType = "2";public static ProductA GetProductA(){// 通过反射避免了手动添加分支string className = "Namespace.ConcreteProductA" + ProductType;return (ProductA) Assembly.Load("CSharpPractice").CreateInstance(className);}public static ProductB GetProductB(){string className = "Namespace.ConcreteProductB" + ProductType;return (ProductB) Assembly.Load("CSharpPractice").CreateInstance(className);}
}

2.4 建造者模式

建造者模式(Builder),将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

建造者模式的结构图如下。对于不同的产品需要不同的生产线,BuilderABuilderB就是两条不同的生产线。而Director则负责确定组装顺序的工作。

示例代码如下

// 产品类,一个产品有多个部件
public class Product
{private readonly List<string> _parts = new();// 添加部件public void AddPart(string part){_parts.Add(part);}// 展示产品public void Show(){Console.WriteLine("产品包含如下部件:");foreach (var part in _parts){Console.WriteLine(part);}}
}
// 抽象建造者类,具体建造什么部件由子类决定
public abstract class Builder
{public abstract void BuildPartA();public abstract void BuildPartB();public abstract Product GetResult();
}
// 建造者1
public class BuilderA : Builder
{private readonly Product _product = new Product();public override void BuildPartA(){_product.AddPart("部件A");}public override void BuildPartB(){_product.AddPart("部件B");}public override Product GetResult(){return _product;}
}
// 建造者2
public class BuilderB : Builder
{private readonly Product _product = new Product();public override void BuildPartA(){_product.AddPart("部件X");}public override void BuildPartB(){_product.AddPart("部件Y");}public override Product GetResult(){return _product;}
}
// 指挥者,用来指挥建造过程
public class Director
{public void Construct(Builder builder){builder.BuildPartA();builder.BuildPartB();}
}

客户端调用如下

Director director = new Director();  
Builder builderA = new BuilderA();  
Builder builderB = new BuilderB();  director.Construct(builderA);  
var productA = builderA.GetResult();  
productA.Show();  director.Construct(builderB);  
var productB = builderB.GetResult();  
productB.Show();  // 输出结果:  
// 产品包含如下部件:  
// 部件A  
// 部件B  
// 产品包含如下部件:  
// 部件X  
// 部件Y

建造者模式是在当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时适用的模式。

建造者模式的好处就是使得建造代码与表示代码分离,由于建造者隐藏了该产品是如何组装的,所以若需要改变一个产品的内部表示,只需要再定义一个具体的建造者就可以了。

2.5 原型模式

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

原型模式的结构图如下。IPrototype接口声明了Clone()克隆方法,实现类ConcretePrototypeClone()方法进行实现,将值类型的数据拷贝到克隆体中。如果数据中包含引用类型,还需要考虑递归克隆的情况。

代码示例如下

// 原型接口,声明克隆方法
public interface IPrototype
{public IPrototype Clone();
}
// 具体原型类
public class ConcretePrototype:IPrototype
{public string Id;public ConcretePrototype(string id){Id = id;}public IPrototype Clone(){// 浅复制return (IPrototype)this.MemberwiseClone();}
}

客户端调用方式如下

ConcretePrototype p1 = new ConcretePrototype("123456");  
ConcretePrototype p2 = (ConcretePrototype)p1.Clone();  Console.WriteLine(p1.Id);  
Console.WriteLine(p2.Id);  
// 输出结果:  
// 123456  
// 123456

原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需知道任何创建的细节。一般在初始化的信息不发生变化的情况下,克隆是最好的办法。这既隐藏了对象创建的细节,又对性能是大大的提高。

2.6 单例模式

单例模式(Singleton),保证一个类仅有一个实例,并提供一个访问它的全局访问点。

单例模式的结构图如下。Singleton持有了一个自身的静态实例,同时将构造方法私有化。当外接需要获取Singleton的实例时,就返回已经持有的静态实例(如果没有就实例化)。这样外部获取的Singleton的实例永远只是固定的一个。

示例代码如下

public class Singleton
{// 持有一个自己的实例private static Singleton _instance;// 构造函数私有化防止外部创建实例private Singleton(){}// 获取唯一实例public static Singleton GetInstance(){if (_instance == null){_instance = new Singleton();}return _instance;}
}

客户端调用如下

Singleton s1 = Singleton.GetInstance();  
Singleton s2 = Singleton.GetInstance();  Console.WriteLine(s1 == s2);
// 输出结果:
// True

上面这种方式只在需要实例时才进行初始化,被成为懒汉式单例类,还有一种方式是在类加载时就进行实例化(静态初始化),这种方式称为饿汉式单例类。

// 饿汉式单例类,加sealed关键字防止派生增加实例
public sealed class Singleton2
{// 第一次引用类的任何成员时创建实例private static readonly Singleton2 _instance = new Singleton2();// 构造函数私有化防止外部创建实例private Singleton2(){}// 获取唯一实例public static Singleton2 GetInstance(){return _instance;}
}

单例模式因为Singleton类封装它的唯一实例,这样它可以严格地控制客户怎样访问它以及何时访问它。简单地说就是对唯一实例的受控访问。

三、结构型模式

3.1 适配器模式

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

适配器模式结构图如下。当客户端需要持有一个Target对象,但现在只有一个Origin对象。为了将Origin对象“伪装”成Target类型,我们需要一个中间的适配器AdapterAdapter持有Origin对象,同时又继承了Target类。在Adapter类重写的方法中调用Origin对象的对应方法。现在,客户端只需要持有Adapter对象即完成了适配。

示例代码如下

// 客户端期待的接口或类
public class Target
{public virtual void TargetRequest(){Console.WriteLine("Target请求");}
}
// 需要适配的接口或类
public class Origin
{public virtual void OriginRequest(){Console.WriteLine("Origin请求");}
}
// 适配器
public class Adapter : Target
{private Origin _origin = new Origin();// 表面调用TargetRequest(),实际调用OriginRequest()public override void TargetRequest(){_origin.OriginRequest();}
}

客户端调用如下

Target target = new Adapter();  
target.TargetRequest();  
// 输出结果:  
// Origin请求

系统的数据和行为都正确,但接口不符时,我们应该考虑用适配器,目的是使控制范围之外的一个原有对象与某个接口匹配。适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况。

3.2 桥接模式

桥接模式(Bridge),将抽象部分与它的实现部分分离,使它们都可以独立地变化。这里的实现指的是抽象类和它的派生类用来实现自己的对象。

这里的“抽象”与“实现”有些让人摸不着头脑。我们通过一个具体的例子来加以理解。假设有一个形状类Shape,它可以扩展出两个子类:圆形Circle和方形Square。现在,我们要对类层次进行扩展,使其包含颜色,比如红色Red和蓝色Blue。那么这两个颜色就需要和之前的形状进行组合,最后形成:红色圆形RedShape、红色方形RedSquare、蓝色圆形BlueCircle、蓝色方形BlueSquare四个类。显然这是个笨办法,因为每扩展一种维度,类的数量就会呈指数增长。

造成这种现象的原因是因为我们试图从多个独立的维度上扩展类。而更明智的做法是抽取其中的某些维度作为独立的类,从而我们就可以在初始类中引用这些维度的对象。这就是桥接模式的基本思想。

桥接模式结构图如下。这里的抽象Abstraction可以看做形状类,Implementor就相当于颜色类。Abstraction通过持有Implementor对象,使自己拥有了Implementor的状态和行为。

示例代码如下

// 实现  
public abstract class Implementor  
{  public abstract void Operation();  
}
// 具体实现
public class ImplementorA : Implementor
{public override void Operation(){Console.WriteLine("具体实现A");}
}
public class ImplementorB : Implementor
{public override void Operation(){Console.WriteLine("具体实现B");}
}
// 抽象
class Abstraction
{protected Implementor Implementor;public void  SetImplementor(Implementor implementor){Implementor = implementor;}public virtual void Operation(){Implementor.Operation();}
}
// 被提炼的抽象
class RefinedAbstraction : Abstraction
{public override void Operation(){Implementor.Operation();}
}

客户端调用如下

Abstraction ab = new RefinedAbstraction();  // 设置具体的实现  
ab.SetImplementor(new ConcreteImplementorA());  
ab.Operation();  ab.SetImplementor(new ConcreteImplementorB());  
ab.Operation();  // 输出结果:  
// 具体实现A  
// 具体实现B

如果你想要拆分或重组一个具有多重功能的庞杂类 (例如能与多个数据库服务器进行交互的类),或希望在几个独立维度上扩展一个类,又或者需要在运行时切换不同实现方法就可以尝试使用这个模式。

3.3 组合模式

组合模式(Composite),将对象组合成树形结构以表示‘部分-整体’的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

假设我们需要统计公司的人员信息,最笨的办法就是挨个人员统计。一般都会想到将统计任务交给下级部门,要求下级部门完成后直接上报结果。从公司级->院级->部门层层下放。然后最底层的层级将本层的人员信息统计完成后上交。这其实就是组合模式的思想。

组合模式的结构图如下。Component描述了叶节点和容器节点的共有行为。Leaf是叶节点,叶节点不会再包含其他叶节点或容器。Composite是容器节点,它可以包含其他容器或叶节点。事实上叶节点只能执行Display()操作,之所以将添加和删除的方法也写进抽象类,是为了消除叶节点和容器节点在抽象层次的区别。

示例代码如下

// 组件抽象类
public abstract class Component
{protected string Name;public Component(string name){Name = name;}public abstract void Add(Component c);public abstract void Remove(Component c);public abstract void Display(int depth);
}
// 叶节点类
public class Leaf : Component
{public Leaf(string name) : base(name){}// 叶节点本身无法添加或删除节点,这样做是为了消除叶节点和容器节点在抽象层次的区别public override void Add(Component c){Console.WriteLine("无法添加节点");}public override void Remove(Component c){Console.WriteLine("无法删除节点");}// 显示叶节点public override void Display(int depth){Console.WriteLine(new string('-',depth)+Name);}
}
// 容器节点类
public class Composite : Component
{// 用来存储子对象private readonly List<Component> _children = new();public Composite(string name) : base(name){}public override void Add(Component c){_children.Add(c);}public override void Remove(Component c){_children.Remove(c);}public override void Display(int depth){Console.WriteLine(new string('-',depth)+Name);foreach (var child in _children){child.Display(depth+2);}}
}

客户端调用如下

Composite root = new Composite("Root");  
root.Add(new Leaf("C"));  
root.Add(new Leaf("D"));  var compositeA = new Composite("A");  
compositeA.Add(new Leaf("E"));  
compositeA.Add(new Leaf("F"));  
root.Add(compositeA);  var compositeB = new Composite("B");  
compositeB.Add(new Leaf("G"));  
compositeB.Add(new Leaf("H"));  
root.Add(compositeB);  root.Display(1);  // 输出结果:  
// -Root  
// ---C  
// ---D  
// ---A  
// -----E  
// -----F  
// ---B  
// -----G  
// -----H

当你发现需求中是体现部分与整体层次的结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑用组合模式了。

组合模式的优点是它就定义了基本对象组合对象的类层次结构。基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断地递归下去,客户代码中,任何用到基本对象的地方都可以使用组合对象。用户是不用关心到底是处理一个叶节点还是处理一个组合组件,也就用不着为定义组合而写一些选择判断语句了。

3.4 装饰模式

装饰模式(Decorator),通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为。

装饰模式就像是俄罗斯套娃,可以通过装饰器对原本的内容层层包装。就像是我们在家可以只穿一身秋衣,如果觉得冷的话还可以套一身保暖衣,如果要出门的话还可以再套一层羽绒服。这就是装饰模式的基本思想。这种模式在数据的加密和压缩中很有用。

装饰模式的结构图如下。ConcreteComponent就是最原始的内容,ConcreteDecoratorAConcreteDecoratorB都可以对其进行装饰。

示例代码如下

public abstract class Component
{public abstract void Operation();
}
// 原始组件
public class ConcreteComponent : Component
{public override void Operation(){Console.WriteLine("ConcreteComponent");}
}
// 抽象装饰类
public abstract class Decorator : Component
{protected Component? Component;public void SetComponent(Component component){Component = component;}public override void Operation(){// 执行的是被装饰对象的Operation()Component?.Operation();}
}
// 具体装饰类
public class ConcreteDecoratorA : Decorator
{public override void Operation(){base.Operation();Console.WriteLine("ConcreteDecoratorA");}
}
public class ConcreteDecoratorB : Decorator
{public override void Operation(){base.Operation();Console.WriteLine("ConcreteDecoratorB");}
}

客户端调用如下

// 原始数据  
ConcreteComponent c = new ConcreteComponent();  
// 装饰类对象  
ConcreteDecoratorA a = new ConcreteDecoratorA();  
ConcreteDecoratorB b = new ConcreteDecoratorB();  // 开始套娃(装饰)  
a.SetComponent(c);  
b.SetComponent(a);  
b.Operation();  // 输出结果:  
// ConcreteComponent  
// ConcreteDecoratorA  
// ConcreteDecoratorB

装饰模式是利用SetComponent来对对象进行包装的。这样每个装饰对象的实现就和如何使用这个对象分离开了,每个装饰对象只关心自己的功能,不需要关心如何被添加到对象链当中。

装饰模式是为已有功能动态地添加更多功能的一种方式。它可以有效地把类的核心职责和装饰功能区分开。而且可以去除相关类中重复的装饰逻辑。

3.5 外观模式

外观模式(Facade),为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

外观模式很容易理解,就是用一个外观类将原本复杂的功能整合到一起。外观模式的结构图如下

代码示例如下

// 子系统
class SubSystemOne
{public void MethodOne(){Console.WriteLine(" 子系统方法一");}
}class SubSystemTwo
{public void MethodTwo(){Console.WriteLine(" 子系统方法二");}
}class SubSystemThree
{public void MethodThree(){Console.WriteLine(" 子系统方法三");}
}class SubSystemFour
{public void MethodFour(){Console.WriteLine(" 子系统方法四");}
}
// 外观类
public class Facade
{private SubSystemOne _one;private SubSystemTwo _two;private SubSystemThree _three;private SubSystemFour _four;public Facade(){_one = new SubSystemOne();_two = new SubSystemTwo();_three = new SubSystemThree();_four = new SubSystemFour();}public void Method1(){_one.MethodOne();_two.MethodTwo();_three.MethodThree();}public void Method2(){_one.MethodOne();_four.MethodFour();}
}

客户端调用如下

Facade facade = new Facade();  
facade.Method1();  
Console.WriteLine("————————————");  
facade.Method2();  // 输出结果:  
// 子系统方法一  
// 子系统方法二  
// 子系统方法三  
// ————————————  
// 子系统方法一  
// 子系统方法四

在下面几种情况下可以考虑使用外观模式:
首先,在设计初期阶段,应该要有意识的将不同的两个层分离,比如经典的三层架构,就需要考虑在数据访问层和业务逻辑层、业务逻辑层和表示层的层与层之间建立外观Facade,这样可以为复杂的子系统提供一个简单的接口,使得耦合大大降低。

其次,在开发阶段,子系统往往因为不断的重构演化而变得越来越复杂,大多数的模式使用时也都会产生很多很小的类,这本是好事,但也给外部调用它们的用户程序带来了使用上的困难,增加外观Facade可以提供一个简单的接口,减少它们之间的依赖。

第三,在维护一个遗留的大型系统时,可能这个系统已经非常难以维护和扩展了,但因为它包含非常重要的功能,新的需求开发必须要依赖于它。此时用外观模式Facade也是非常合适的。你可以为新系统开发一个外观Facade类,来提供设计粗糙或高度复杂的遗留代码的比较清晰简单的接口,让新系统与Facade对象交互,Facade与遗留代码交互所有复杂的工作。

3.6 享元模式

享元模式(Flyweight),运用共享技术有效地支持大量细粒度的对象。

假设我们要开发一款飞机大战的游戏。游戏中的飞机可以发射多种弹药。假如每发射一枚弹药就实例化一枚子弹,那么当屏幕中的子弹数量较多时,就会消耗大量的性能。事实上对于一枚子弹来说,改变的只有其在场景中的坐标。而子弹的贴图、颜色等对于同一种弹药来说是完全相同的。所以我们完全可以将这些不变的属性抽象出来,形成享元类,通过引用的方式添加到子弹类中。然后通过一个享元工厂生产这些享元类。事实上,享元工厂中只需要为每种类型的子弹创建一个享元对象就足够了。因为享元对象的数据并不会被修改,所以所有的子弹都可以共享这几个享元类对象。这样就节省了许多性能。

享元模式的结构图如下。

示例代码如下

// 享元类
public class Flyweight
{public int Shared1 { get; }public int Shared2 { get; }public Flyweight(int shared1, int shared2){Shared1 = shared1;Shared2 = shared2;}
}
// 享元工厂
public class FlyweightFactory
{private readonly Dictionary<string,Flyweight> _flyweights = new();public FlyweightFactory(){// 初始化时生成三个实例_flyweights.Add("X",new Flyweight(1,2));_flyweights.Add("Y",new Flyweight(3,4));_flyweights.Add("Z",new Flyweight(5,6));}public Flyweight GetFlyweight(string key){return _flyweights[key];}
}
// 包含享元的上下文类
public class Context
{// 外在状态public int Unique;// 享元队先后private Flyweight _shared;public Context(int unique,Flyweight shared){Unique = unique;_shared = shared;}public void Operation(){Console.WriteLine($"外在状态:{Unique} 共享状态:{_shared.Shared1} {_shared.Shared2}");}
}

客户端调用如下

FlyweightFactory factory = new FlyweightFactory();  
Context context1 = new Context(1, factory.GetFlyweight("X"));  
Context context2 = new Context(2, factory.GetFlyweight("Y"));  
Context context3 = new Context(3, factory.GetFlyweight("Z"));  context1.Operation();  
context2.Operation();  
context3.Operation();  // 输出结果:  
// 外在状态:1 共享状态:1 2  
// 外在状态:2 共享状态:3 4  
// 外在状态:3 共享状态:5 6

享元模式可以避免大量非常相似类的开销。在程序设计中,有时需要生成大量细粒度的类实例来表示数据。如果能发现这些实例除了几个参数外基本上都是相同的,有时就能够受大幅度地减少需要实例化的类的数量。如果能把那些参数移到类实例的外面,在方法调用时将它们传递进来,就可以通过共享大幅度地减少单个实例的数目。

使用享元模式需要维护一个记录了系统已有的所有享元的列表,而这本身需要耗费资源,另外享元模式使得系统更加复杂。为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化。因此,应当在有足够多的对象实例可供共享时才值得使用享元模式。

3.7 代理模式

代理模式(Proxy),为其他对象提供一种代理以控制对这个对象的访问。

代理模式的结构图如下。代理类通过持有实体类对象,调用真正的服务方法,并对外暴露一个代理的服务方法。在代理方法中,可以进行一些额外的操作。

代码示例

public interface ISubject
{public void Request();
}
// 真实实体
public class RealSubject : ISubject
{public void Request(){Console.WriteLine("真实的请求");}
}
// 代理
public class Proxy : ISubject
{private RealSubject? _realSubject;public void Request(){if (_realSubject == null){_realSubject = new RealSubject();}_realSubject.Request();}
}

客户端调用

Proxy proxy = new Proxy();  
proxy.Request();  
// 输出结果:  
// 真实的请求

应用场景:

  • 远程代理,也就是为一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存在于不同地址空间的事实。
  • 虚拟代理,是根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象。
  • 安全代理,用来控制真实对象访问时的权限。
  • 智能指引,是指当调用真实的对象时,代理处理另外一些事。

代理模式其实就是在访问对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。

四、行为型模式

4.1 职责链模式

职责链模式(Chain of Responsibility):使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

职责链模式结构图如下。处理者可以通过SetSuccessor()设置下一位处理者,在HandleRequest()时决定是自己处理或交给下一个处理者进行处理。

示例代码如下

// 处理者接口
public abstract class Handler
{// 继任者protected Handler Successor;public void SetSuccessor(Handler successor){Successor = successor;}// 处理请求public abstract void HandleRequest(int request);
}
// 具体处理者
public class ConcreteHandler1 : Handler
{public override void HandleRequest(int request){if (request < 10){Console.WriteLine("ConcreteHandler1处理请求:"+request);}else{// 交给下一位处理Successor.HandleRequest(request);}}
}
public class ConcreteHandler2 : Handler
{public override void HandleRequest(int request){if (request >= 10){Console.WriteLine("ConcreteHandler2处理请求:"+request);}else{// 交给下一位处理Successor.HandleRequest(request);}}
}

客户端调用如下

Handler handler1 = new ConcreteHandler1();  
Handler handler2 = new ConcreteHandler2();  handler1.SetSuccessor(handler2);  handler1.HandleRequest(1);// handler1处理  
handler1.HandleRequest(11);// handler2处理  // 输出结果:  
// ConcreteHandler1处理请求:1  
// ConcreteHandler2处理请求:11

对于职责链模式来说,当客户提交一个请求时,请求是沿链传递直至有一个ConcreteHandler对象负责处理它。这就使得接收者和发送者都没有对方的明确信息,且链中的对象自己也并不知道链的结构。结果是职责链可简化对象的相互连接,它们仅需保持一个指向其后继者的引用,而不需保持它所有的候选接受者的引用,这也就大大降低了耦合度。

另外,由于是在客户端来定义链的结构,所以可以随时地增加或修改处理一个请求的结构。增强了给对象指派职责的灵活性。但这也可能导致一个请求极有可能到了链的末端都得不到处理,或者因为没有正确配置而得不到处理。

4.2 命令模式

命令模式(Command),可将请求转换为一个包含与请求相关的所有信息的独立对象。 该转换让你能根据不同的请求将方法参数化、 延迟请求执行或将其放入队列中, 且能实现可撤销操作。

命令模式的结构图如下。Invoker是命令的触发者,它无需直接向Receiver发送请求,而是通过触发一个命令来执行这个请求。Command类中包含了对Receiver的引用,当命令被触发时,就去请求Receiver

示例代码如下

// 命令执行所需的类
public class Receiver
{public void Action(){Console.WriteLine("执行命令");}
}
// 命令抽象类
public abstract class Command
{protected Receiver Receiver;public Command(Receiver receiver){Receiver = receiver;}// 执行命令public abstract void Execute();
}
// 具体命令
class ConcreteCommand : Command
{public ConcreteCommand(Receiver receiver) : base(receiver){ }public override void Execute(){Receiver.Action();}
}
// 命令发起者
class Invoker
{private Command _command;public void SetCommand(Command command){_command = command;}public void ExecuteCommand(){_command.Execute();}
}

客户端调用如下

Command command = new ConcreteCommand(new Receiver());
Invoker invoker = new Invoker();invoker.SetCommand(command);
invoker.ExecuteCommand();

命令模式的优点如下:

  • 它能较容易地设计一个命令队列。
  • 在需要的情况下,可以较容易地将命令记入日志。
  • 允许接收请求的一方决定是否要否决请求。
  • 可以容易地实现对请求的撤销和重做。
  • 由于加进新的具体命令类不影响其他的类,因此增加新的具体命令类很容易。
  • 最重要的是命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分割开。

4.3 迭代器模式

迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。

迭代器模式结构图如下。迭代器类集成了遍历集合所需的所有操作。集合对象可以通过CreateIterator()方法创建一个自己的迭代器,并将自己作为参数传入。创建出的迭代器对象持有了集合对象的引用,从而可以进行对这个集合对象的遍历操作。

示例代码如下

// 迭代器抽象类
public abstract class Iterator
{// 得到起始元素public abstract object? First();// 得到下一元素public abstract object? Next();// 判断是否到达结尾public abstract bool IsDone();// 返回当前元素public abstract object? CurrentItem();
}
// 聚集抽象类
public abstract class Aggregate
{// 创建迭代器public abstract Iterator CreateIterator();
}
// 具体迭代器类
public class ConcreteIterator : Iterator
{private readonly ConcreteAggregate _aggregate;private int _current = 0;public ConcreteIterator(ConcreteAggregate aggregate){_aggregate = aggregate;}public override object? First(){if (_aggregate.Count == 0) return null;return _aggregate[0];}public override object? Next(){object? res = null;_current++;if (_current < _aggregate.Count){res = _aggregate[_current];}return res;}public override bool IsDone(){return _current >= _aggregate.Count;}public override object? CurrentItem(){if (_aggregate.Count == 0) return null;return _aggregate[_current];}
}
// 具体聚集类
public class ConcreteAggregate : Aggregate
{private readonly IList<object> _items = new List<object>();public int Count => _items.Count;public object this[int index]{get => _items[index];set => _items.Insert(index, value);}public override Iterator CreateIterator(){return new ConcreteIterator(this);}
}

客户端调用如下

// 聚集对象
ConcreteAggregate aggregate = new ConcreteAggregate();
aggregate[0] = "A";
aggregate[1] = "B";
aggregate[2] = "C";
aggregate[3] = "D";Iterator iterator = new ConcreteIterator(aggregate);while (!iterator.IsDone())
{Console.WriteLine(iterator.CurrentItem());iterator.Next();
}
// 输出结果:
// ABCD

迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。

当需要访问一个聚集对象,而且不管这些对象是什么都需要遍历的时候,或是需要对聚集有多种方式遍历时,可以考虑用迭代器模式。

4.4 中介者模式

中介者模式(Mediator),用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

中介者模式结构图如下。Mediator作为中介者,需要事先“认识”两个组件对象。当两个组件需要进行通讯时,就通过中介者进行消息的传达。

示例代码如下

// 抽象中介者类
public abstract class Mediator
{public abstract void Send(string message, Component component);
}
// 抽象组件
public abstract class Component
{protected Mediator Mediator;public Component(Mediator mediator){Mediator = mediator;}// 通过中介者发送消息public abstract void Send(string message);// 通过中介者接收消息public abstract void Notify(string message);
}
// 具体中介者类
public class ConcreteMediator : Mediator
{public ConcreteComponent1 Component1 { get; set; }public ConcreteComponent2 Component2 { get; set; }public override void Send(string message, Component component){if(component != Component1) Component1.Notify(message);else Component2.Notify(message);}
}
// 具体组件
public class ConcreteComponent1 : Component
{public ConcreteComponent1(Mediator mediator) : base(mediator){}public override void Send(string message){Mediator.Send(message,this);}public override void Notify(string message){Console.WriteLine("组件1接收到消息:"+message);}
}
public class ConcreteComponent2 : Component
{public ConcreteComponent2(Mediator mediator) : base(mediator){}public override void Send(string message){Mediator.Send(message,this);}public override void Notify(string message){Console.WriteLine("组件2接收到消息:"+message);}
}

客户端调用

ConcreteMediator mediator = new ConcreteMediator();  // 让组件认识中介者  
ConcreteComponent1 component1 = new ConcreteComponent1(mediator);  
ConcreteComponent2 component2 = new ConcreteComponent2(mediator);  // 让中介者认识组件  
mediator.Component1 = component1;  
mediator.Component2 = component2;  component1.Send("吃了吗您内");// 组件2接收到消息:吃了吗您内  
component2.Send("吃了");// 组件1接收到消息:吃了

中介者模式的优点首先是Mediator的出现减少了各个Component的耦合,使得可以独立地改变和复用各个Component类和Mediator。其次由于把对象如何协作进行了抽象,将中介作为一个独立的概念并将其封装在一个对象中,这样关注的对象就从对象各自本身的行为转移到它们之间的交互上来,也就是站在一个更宏观的角度去看待系统。

但它也有缺点。由于ConcreteMediator控制了集中化,于是就把交互复杂性变为了中介者的复杂性,这就使得中介者会变得比任何一个ConcreteComponent都复杂。

4.5 备忘录模式

备忘录(Memento):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。

备忘录模式结构图如下。这个模式比较容易理解,就是通过一个外部类保存指定类的某些状态,然后交给一个统一的类进行管理。这个管理类可以帮助原发器进行状态回溯。

示例代码如下

// 备忘录的创建者
public class Originator
{// 需要保存的属性public string State { get; set; }// 创建备忘录public Memento CreateMemento(){return new Memento(State);}// 恢复备忘录public void SetMemento(Memento memento){State = memento.State;}public void Show(){Console.WriteLine("State:"+State);}
}// 备忘录
public class Memento
{public string State { get; }public Memento(string state){State = state;}
}
// 备忘录管理者
public class CareTaker
{public Memento Memento { get; set; }
}

客户端调用如下

Originator originator = new Originator();  
originator.State = "状态A";  
originator.Show();  // 保存状态  
CareTaker careTaker = new CareTaker();  
careTaker.Memento = originator.CreateMemento();  originator.State = "状态B";  
originator.Show();  // 恢复状态  
originator.SetMemento(careTaker.Memento);  
originator.Show();  
// 输出结果:  
// State:状态A  
// State:状态B  
// State:状态A

Memento模式比较适用于功能比较复杂的,但需要维护或记录属性历史的类,或者需要保存的属性只是众多属性中的一小部分时,Originator可以根据保存的Memento信息还原到前一状态。但如果状态数据很大很多,那么备忘录对象会非常耗内存。

4.6 观察者模式

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

观察者模式的结构图如下。这个模式也很好理解,通知者就像一个群聊,负责将消息通知到群聊里的每一个观察者。

示例代码如下

// 抽象观察者
public abstract class Observer
{// 更新public abstract void Update();
}
// 抽象通知者
public abstract class Subject
{private readonly List<Observer> _observers = new();// 添加观察者public void Add(Observer observer){_observers.Add(observer);}// 删除观察者public void Delete(Observer observer){_observers.Remove(observer);}// 通知观察者public void Notify(){foreach (var observer in _observers){observer.Update();}}
}
// 具体观察者
public class ConcreteObserver:Observer
{private string _name;private string? _observeState;private ConcreteSubject _subject;public ConcreteObserver(string name,ConcreteSubject subject){_name = name;_subject = subject;}public override void Update(){_observeState = _subject.SubjectState;Console.WriteLine($"观察者 {_name} 的新状态为 {_observeState}");}
}
// 具体通知者
public class ConcreteSubject : Subject
{public string? SubjectState { get; set; }
}

客户端调用如下

ConcreteSubject subject = new ConcreteSubject();  subject.Add(new ConcreteObserver("A",subject));  
subject.Add(new ConcreteObserver("B",subject));  
subject.Add(new ConcreteObserver("C",subject));  subject.SubjectState = "ABC";  
subject.Notify();  // 输出结果:  
// 观察者 A 的新状态为 ABC
// 观察者 B 的新状态为 ABC
// 观察者 C 的新状态为 ABC

当一个对象的改变需要同时改变其他对象,而且它不知道具体有多少对象有待改变时,应该考虑使用观察者模式。当一个抽象模型有两个方面,其中一方面依赖于另一方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。

4.7 状态模式

状态模式(State),当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。

状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。

状态模式的结构图如下。每个状态中的Handle()方法都包含让Context转换到下一个状态的逻辑。Context则主要负责当前状态逻辑的执行。

示例代码如下

// 抽象状态类
public abstract class State
{public abstract void Handle(Context context);
}
// 状态管理类
public class Context
{public State CurrentState { get; set; }// 初始化时指定初始状态public Context(State state){CurrentState = state;}// 处理当前状态逻辑public void Request(){CurrentState.Handle(this);}
}
// 状态A
public class StateA : State
{public override void Handle(Context context){Console.WriteLine("当前状态:StateA");// 下一个状态为状态Bcontext.CurrentState = new StateB();}
}
// 状态B
public class StateB : State
{public override void Handle(Context context){Console.WriteLine("当前状态:StateB");// 下一个状态为状态Acontext.CurrentState = new StateA();}
}

客户端调用如下

Context context = new Context(new StateA());  
context.Request();  
context.Request();  
context.Request();  
context.Request();  // 输出结果:  
// 当前状态:StateA  
// 当前状态:StateB  
// 当前状态:StateA  
// 当前状态:StateB

状态模式的好处是将特定的状态相关的行为都放入一个对象中,由于所有与状态相关的代码都存在于某个State中,所以通过定义新的子类可以很容易地增加新的状态和转换。当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式。

4.8 策略模式

策略模式(Strategy):它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。

假设你要开发一款导航应用。使用导航的用户可能是开车,也可能是骑行,也可能是步行。如果将不同出行方式的导航算法集中在一个类中,这个类将会越来越臃肿。一种明智的解决方式是将不同的出行方式的算法分别封装到一个类中。然后通过上下文视情况调用。这就是策略模式的核心思想。

策略模式的结构图如下。

示例代码

public class Context
{private readonly IOperation _operation;public Context(IOperation operation){_operation = operation;}public void Operate(){_operation.Operate();}
}public interface IOperation
{public void Operate();
}public class OperationA:IOperation
{public void Operate(){Console.WriteLine("OperationA");}
}
public class OperationB:IOperation
{public void Operate(){Console.WriteLine("OperationB");}
}
public class OperationC:IOperation
{public void Operate(){Console.WriteLine("OperationC");}
}

对Context类进行改造,结合简单工厂模式。此时客户端无需依赖IOperate接口。

public class Context
{private readonly IOperation _operation;public Context(string type){switch (type){case "A":_operation = new OperationA();break;case "B":_operation = new OperationB();break;case "C":_operation = new OperationC();break;}}public void Operate(){_operation.Operate();}
}

策略模式的优点:

  • 策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。
  • 策略模式的Strategy类层次为Context定义了一系列的可供重用的算法或行为。继承有助于析取出这些算法中的公共功能。
  • 简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。

4.9 模板方法模式

模板方法模式(Template Method),定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

模板方法模式的结构图如下。实现类通过实现不同的模板方法,使最终组装出的“产品”具有不同的“零件”。

代码示例如下

public abstract class AbstractClass
{public abstract void Method1();public abstract void Method2();// 模板方法public void TemplateMethod(){Method1();Method2();Console.WriteLine("模板方法");}
}public class ConcreteClassA : AbstractClass
{public override void Method1(){Console.WriteLine("ConcreteClassA Method1");}public override void Method2(){Console.WriteLine("ConcreteClassA Method2");}
}
public class ConcreteClassB : AbstractClass
{public override void Method1(){Console.WriteLine("ConcreteClassB Method1");}public override void Method2(){Console.WriteLine("ConcreteClassB Method2");}
}

客户端调用如下

AbstractClass a;  
a = new ConcreteClassA();  
a.TemplateMethod();  a = new ConcreteClassB();  
a.TemplateMethod();  // 输出结果:  
// ConcreteClassA Method1  
// ConcreteClassA Method2  
// 模板方法  
// ConcreteClassB Method1  
// ConcreteClassB Method2  
// 模板方法

模板方法模式是通过把不变行为搬移到超类,去除子类中的重复代码来体现它的优势。当不变的和可变的行为在方法的子类实现中混合在一起的时候,不变的行为就会在子类中重复出现。我们通过模板方法模式把这些行为搬移到单一的地方,这样就帮助子类摆脱重复的不变行为的纠缠。

4.10 解释器模式

解释器模式(Interpreter),给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。比如正则表达式就是一个很好的例子,它可以将原本复杂但常用的算法通过一串特定的字符串表示。

解释器模式结构图如下。对于一个表达式R=R1+R2,R1和R2就是终结符,+就是非终结符。

示例代码如下

// 抽象表达式
abstract class AbstractExpression
{// 解释public abstract int Interpret(Context context);
}
// 上下文类
class Context
{private Dictionary<string, int> _map = new();public Context(){_map.Add("1",1);_map.Add("2",2);_map.Add("3",3);}public int Interpret(string key){return _map[key];}
}
// 终结符表达式
class TerminalExpression : AbstractExpression
{private string _key;public TerminalExpression(string key){_key = key;}public override int Interpret(Context context){return context.Interpret(_key);}
}
// 加法非终结符表达式
class PlusNonTerminalExpression : AbstractExpression
{private AbstractExpression _exp1;private AbstractExpression _exp2;public PlusNonTerminalExpression(AbstractExpression exp1, AbstractExpression exp2){_exp1 = exp1;_exp2 = exp2;}public override int Interpret(Context context){return _exp1.Interpret(context) + _exp2.Interpret(context);}
}

客户端调用如下

Context context = new Context();  
var exp1 = new TerminalExpression("1");  
var exp2 = new TerminalExpression("2");  
var res = new PlusNonTerminalExpression(exp1, exp2);  Console.WriteLine("1+2="+res.Interpret(context));  
// 输出结果:  
// 1+2=3

当有一个语言需要解释执行,并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。使用解释器模式可以很容易地改变和扩展文法,因为该模式使用类来表示文法规则,你可使用继承来改变或扩展该文法。也比较容易实现文法,因为定义抽象语法树中各个节点的类的实现大体类似,这些类都易于直接编写。

解释器模式的缺点是为文法中的每一条规则至少定义了一个类,因此包含许多规则的文法可能难以管理和维护。建议当文法非常复杂时,使用其他的技术如语法分析程序或编译器生成器来处理。

4.11 访问者模式

访问者模式(Visitor),表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

访问者模式结构图如下。访问者模式提供了在不同视角下对元素进行不同操作的方法。比如在项目经理的视角下,可能更关心员工的工作效率;而在HR的视角下,可能更关心员工的薪资。这里的项目经理和HR就是访问者,而员工就是具体的元素。如果员工类将不同视角访问自己的操作集成在内部,那么每当多一种视角,就需要修改员工类。而如果将操作定义在访问者类中,就能避免修改员工类,只需要新增访问者类。

示例代码如下

// 访问者抽象类
public abstract class Visitor
{public abstract void VisitConcreteElementA(ConcreteElementA concreteElementA);public abstract void VisitConcreteElementB(ConcreteElementB concreteElementB);
}
// 被访问的元素抽象类
public abstract class Element
{public abstract void Accept(Visitor visitor);
}
// 具体被访问的元素
public class ConcreteElementA:Element
{public override void Accept(Visitor visitor){// 双分派技术实现处理与数据结构分离visitor.VisitConcreteElementA(this);}// 其他专有方法public void OperationA(){}
}
public class ConcreteElementB:Element
{public override void Accept(Visitor visitor){// 双分派技术实现处理与数据结构分离visitor.VisitConcreteElementB(this);}// 其他专有方法public void OperationB(){}
}
// 具体访问者类
public class ConcreteVisitor1 : Visitor
{public override void VisitConcreteElementA(ConcreteElementA concreteElementA){Console.WriteLine($"{concreteElementA.GetType().Name} 被 ConcreteVisitor1 访问");}public override void VisitConcreteElementB(ConcreteElementB concreteElementB){Console.WriteLine($"{concreteElementB.GetType().Name} 被 ConcreteVisitor1 访问");}
}public class ConcreteVisitor2 : Visitor
{public override void VisitConcreteElementA(ConcreteElementA concreteElementA){Console.WriteLine($"{concreteElementA.GetType().Name} 被 ConcreteVisitor2 访问");}public override void VisitConcreteElementB(ConcreteElementB concreteElementB){Console.WriteLine($"{concreteElementB.GetType().Name} 被 ConcreteVisitor2 访问");}
}
// 枚举被访问元素的类
class ObjectStructure
{private readonly IList<Element> _elements = new List<Element>();public void Attach(Element element){_elements.Add(element);}public void Detach(Element element){_elements.Remove(element);}public void Accept(Visitor visitor){foreach (Element e in _elements){e.Accept(visitor);}}
}

客户端调用如下

ObjectStructure obj = new ObjectStructure();  
obj.Attach(new ConcreteElementA());  
obj.Attach(new ConcreteElementB());  ConcreteVisitor1 v1 = new ConcreteVisitor1();  
ConcreteVisitor2 v2 = new ConcreteVisitor2();  obj.Accept(v1);  
obj.Accept(v2);  // 输出结果:  
// ConcreteElementA 被 ConcreteVisitor1 访问  
// ConcreteElementB 被 ConcreteVisitor1 访问  
// ConcreteElementA 被 ConcreteVisitor2 访问  
// ConcreteElementB 被 ConcreteVisitor2 访问

访问者模式的目的是要把处理从数据结构分离出来。很多系统可以按照算法和数据结构分开,如果这样的系统有比较稳定的数据结构,又有易于变化的算法的话,使用访问者模式就是比较合适的,因为访问者模式使得算法操作的增加变得容易。增加新的操作只需要增加一个新的访问者。

五、参考资料

[1].《大话设计模式》
[2]. https://refactoringguru.cn/design-patterns

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

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

统一异常处理和统一日志处理

一、什么是统一异常处理 1、制造异常 除以0 int a 10/0; 2、什么是统一异常处理 我们想让异常结果也显示为统一的返回结果对象&#xff0c;并且统一处理系统的异常信息&#xff0c;那么需要统一异常处理 二、统一异常处理 1、创建统一异常处理器 /*** 统一异常处理类*/ …...

Linux C 数据结构——二叉树

先放这张图&#xff1a; 可以看出&#xff0c;树是非线性结构&#xff1b; 一、树的概念 树&#xff08;tree&#xff09;是n(n>0)个节点的有限集合T&#xff0c;它满足两个条件&#xff1a; 1&#xff09;有且仅有一个特定的称为根&#xff08;root&#xff09;的节点&am…...

基于asp.net的中小学生假期安全教育在线平台

孩子时代总是那么的贪玩&#xff0c;尤其是在假期做完作业之后&#xff0c;三五个玩伴相聚在一起各种危险的活动就要开始了&#xff0c;每年的寒暑假都是中小学生的事故频发期&#xff0c;因为这个时候是他们最自由的时候&#xff0c; 不用再背负承重的课业了&#xff0c;所以开…...

spss数据预处理步骤_数据预处理详解

简介由于当今数据的数量庞大且来自于各种不同类型的来源&#xff0c;因此出现数据异常的可能性不断增加。鉴于高质量数据可生成更好的模型和预测&#xff0c;数据预处理的重要性与日俱增&#xff0c;并且已经成为数据科学/机器学习/AI 管道中的基本步骤。在本文中&#xff0c;我…...

申宝股票-CRO概念股持续拉升

周二三大指数集体高开&#xff0c;开盘后弱势震荡&#xff0c;一度集体翻绿&#xff0c;板块方面&#xff0c;数字货币概念股在利好刺激下冲高&#xff0c;盐湖提锂概念股拉升&#xff0c;电力、煤炭板块持续下行。午后两市回暖&#xff0c;创指涨逾1%&#xff0c;北向资金持续…...

maven exclusions version

<dependency><groupId>cn.wonhigh</groupId><artifactId>base-framework-web</artifactId><version>${base.version}</version> <exclusions><exclusion><artifactId>base-framework-dal</artifactId><...

log4j2日志

1.先导入需要的jar包,pom.xml <dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.17.1</version></dependency>2.在CLASSPATH下建立log4j2.xml【resources下建立】 完…...

react的非受控组件和受控组件区别

10-非受控组件>表单数据将交由 DOM 节点来处理 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewpor…...

二维数组初始化规则

二维数组初始化的形式为&#xff1a; 数据类型 数组名[整常量表达式][ 整常量表达式]{ 初始化数据 }&#xff1b; 在{ }中给出各数组元素的初值&#xff0c;各初值之间用逗号分开。把{ }中的初值依次赋给各数组元素。 有如下几种初始化方式&#xff1a; ⑴ 分行进行初始化 int …...

java log4j 输出级别_Log4j日志级别详解

Log4j日志级别从高到低分别为&#xff1a;8&#xff1a;OFF(Integer.MAX_VALUE)&#xff0c;用户关闭所有日志记录7&#xff1a;FATAL(50000)&#xff0c;导致应用程序退出的严重错误6&#xff1a;ERROR(40000)&#xff0c;虽然发生了错误但不影响系统的继续运行5&#xff1a;W…...

bzero和memset的区别

1)void *memset(void *s,int c,size_t n) 总的作用&#xff1a;将已开辟内存空间 s 的首 n 个字节的值设为值 c。 2).memset() 函数常用于内存空间初始化。如&#xff1a; char str[100]; memset(str,0,100); 3).memset可以方便的清空一个结构类…...

PHP高级工程师的需知道的

PHP高级工程师的需知道的 2010-05-10 11:32:15| 分类&#xff1a; PHP |字号 订阅1. 基本知识点* HTTP协议中几个状态码的含义:503 500 401 200 301 302503:请求超时 500&#xff1a;内部服务错误&#xff0c;一般是php程序错误导致 401&#xff1a;未受权访问 200 :正确响应…...

胶囊网络资源

最好的一个: (整个原理清清楚楚, 明明白白) 【小小】2D胶囊网络论文精度 Dynamic Routing Between Capsules_哔哩哔哩_bilibili 全英文版: (还行) Capsule Networks胶囊网络教程&#xff0c;Hinton本人盛赞&#xff08;英语字幕&#xff09;_哔哩哔哩_bilibili 李宏毅概览…...

32、最长有效括号 | 算法(leetode,附思维导图 + 全部解法)300题

零 标题&#xff1a;算法&#xff08;leetode&#xff0c;附思维导图 全部解法&#xff09;300题之&#xff08;32&#xff09;最长有效括号 一 题目描述 二 解法总览&#xff08;思维导图&#xff09; 三 全部解法 1 方案1 1)代码&#xff1a; // 方案1 “滑动窗口法”。…...

leetcode 687最长同值路径

树的最长路径代表的意思是树的两个结点之间的边的条数&#xff0c; 刚开始我理解错题目的意思了&#xff0c;以为求的是最多联通的结点数。 比如说这组数据&#xff1a;[1,null,1,1,1,1,1,1,null,1] 它的最长路径见下图&#xff1a;class Solution { public:int ans 0;int lon…...

python学习-文件的输入输出

文件的输入输出 文件&#xff1a;数据持久化最简单的类型&#xff0c;也叫平面文件(flat file)。它仅仅是一个文件名下的字节流&#xff0c;把数据从一个文件读入内存&#xff0c;然后从内存写入文件。 读一个文件之前需要打开它&#xff0c;之后可以调用函数来读写数据&#x…...

小球下落开关的c语言,小球下落(二叉树)!!!!

小球下落 !!!问题描述 :有一颗二叉树,做大的深度为D,所有叶子的深度都相同,所有节点从上到下从左到右的编号为 1,2,3,4....2^(D-1)在节点1处放一个小球,他会往下落,每个内节点上都有一个开关,初始化的时候都是关着的,当每次有小球落到一个开关的时候,他的状态就会变化,当小球到…...

C++刷题——基本数据类型、运算符与表达式

文章目录1、输出各种数据类型所占用存储空间的大小2、下列哪一项能用作用户自定义的标识符3、指出下列程序中的错误4、给下列表达式加上全部的括号 (假设所用变量均已定义)5、请根据下列题意写出相应的表达式6、下列选项中两个表达式的运算结果相同的是7、下列程序的运行结果为…...

python实现胶囊网络_在TensorFlow中实现胶囊网络

点击上方“小白学视觉”&#xff0c;选择加"星标"或“置顶”重磅干货&#xff0c;第一时间送达我们都知道&#xff0c;在许多计算机视觉任务中&#xff0c;卷积神经网络(CNN)的性能均优于人类。所有基于CNN的模型都具有与卷积层相同的基本体系结构&#xff0c;其后是…...

ios闹钟铃声实现代码

// // TBAudioPlayer.h // PlayAudio // // Created by 杨飞 on 10/25/12. // Copyright (c) 2012 self. All rights reserved. //#import <Foundation/Foundation.h> #import "AudioToolBox/AudioToolBox.h" #import "PlayAudio.h"class PlayLo…...

SPJ数据库-初识sql语句(05)(注释版)

--1、 /*找出所有供应商的姓名和所在城市*/ select sname,city from s--2、 /*找出所有零件的名称、颜色、重量*/ select pname,color,weight from p--3、 /*找出使用供应商s1所供应零件的工程号码*/ select distinct jno from spj where snos1--4、 /*找出工程项目j2使用的各种…...

习题10-7 十进制转换二进制(15 分)提问

本题要求实现一个函数&#xff0c;将正整数n转换为二进制后输出。 函数接口定义&#xff1a; void dectobin( int n );函数dectobin应在一行中打印出二进制的n。建议用递归实现。 裁判测试程序样例&#xff1a; #include <stdio.h>void dectobin( int n );int main()…...

方阵的特征值和特征向量的求解案例(二阶方阵)

...

跨域请求

/* * Description: 配置文件 */ module.exports { publicPath: "./", devServer: { open: true, proxy: "http://106.15.179.105/api" //跨域路径 }, }; // proxy是代理的意思 // 代理跨域就是在欺骗浏览器 让浏览器认为你访问的还是 同…...

Leetcode 165、比较版本号

Problem Source : https://leetcode-cn.com/problems/compare-version-numbers Solution Source : https://github.com/hujingbo98/algorithm/blob/master/source/leetcode/0165_CompareVersionNumbers.cpp 165、比较版本号 给你两个版本号 version1 和 version2 &#xff0c…...

关于嵌入式学习和规划,求指点?

在知乎上收到的一个提问问题&#xff1a;各位大佬好&#xff0c;我先说说基本情况&#xff0c;28岁&#xff0c;北京&#xff0c;嵌入式软开&#xff0c;军工行业。硕士毕业一年半。工作不忙收获很少&#xff0c;造成我自己特别迷茫&#xff0c;没有了方向&#xff0c;自己学没…...

如何利用知乎口碑营销提升品牌流量

一、回答排名优化 知乎口碑营销利用精准选题系统筛选出和账号属性以及产品相关有热度的问题&#xff0c;然后再围绕问题输出高质量的优质内容之后优化到第一位&#xff0c;然后获得大量的曝光吸引流量。 二、关键词排名优化 知乎用户每天产生一千W次搜索行为&#xff0c;其中…...

java多态的理解

一、多态是什么 面向对象的三大特性是封装、继承、多态。多态&#xff08;polymorphic&#xff09;的定义&#xff1a;父类引用变量可以指向子类对象&#xff0c;允许不同类的对象可以调用同一个方法&#xff0c;即同一个方法可以根据调用对象的不同而有不同的实现形式。实现多…...

2022年专精特新各地区补贴政策汇总

近期&#xff0c;专精特新的补贴政策吸引了很多企业前来办理&#xff0c;部分地区的企业申请专精特新是有高额补贴的&#xff0c;因此有不少的企业都是来咨询同邦信息科技的小编都有哪些地区可以领取补贴&#xff0c;今天小编就给大家整理一下专精特新的补贴 广东省 预计申报时…...

csv 文件介绍

CSV即Comma Separate Values&#xff0c;这种文件格式经常用来作为不同程序之间的数据交互的格式。 具体文件格式 每条记录占一行 以逗号为分隔符 逗号前后的空格会被忽略 字段中包含有逗号&#xff0c;该字段必须用双引号括起来 字段中包含有换行符&#xff0c;该字段必须用双…...

停更6天我是干嘛去了?当然是为了给大家整理资料去了!史上最全阿里Java面试题总结及答案!

最近呀&#xff0c;老是有粉丝私信问我&#xff1a;“最近你平台支持这么大&#xff0c;粉丝蹭蹭涨&#xff0c;怎么说停更就停更了呢&#xff1f;” 我&#xff1a;“没办法啊&#xff0c;最近有粉丝在这种面经分享平台收集了一些阿里的面试题&#xff0c;说请我马上整理出来…...

从零构建通讯器--4.3日志打印实战,捋下main函数的调用顺序

文章目录一&#xff1a;基础设施之日志打印实战代码一&#xff08;1&#xff09;新文件&#xff08;2&#xff09;&#xff08;//ngx_log_stderr() :三个特殊文件描述符【三章七节】&#xff08;3&#xff09;printf的例子说明&#xff08;4&#xff09;ngx_log_stderr()说明和…...

深度linux node,二叉树的深度_Linux编程_Linux公社-Linux系统门户网站

输入一棵二叉树的根结点&#xff0c;求该树的深度&#xff0c;从根结点到叶结点依次经过的结点(含根&#xff0c;叶结点)形成树的一条路径&#xff0c;最长路径的长度为树的深度#include using namespace std;struct BinaryTreeNode{int data;struct BinaryTreeNode *lchild;st…...

ROS1云课→10日志信息

ROS1云课→09功能包小定制&#xff08;CLI命令行接口&#xff09; 使用console 运行ROS1各类应用后&#xff0c;消息会分类显示&#xff1a; 通过这类信息可以查看机器人系统过程中的各种警报信息等。 日志信息 通过信息记录显示程序的运行状态是好的习惯&#xff0c;但需要确…...

机器学习的相关软件框架下载安装

文章目录一、Anaconda1. Anaconda 的下载2. Anaconda 的安装3. Anaconda Navigator 打不开问题&#xff08;不适用所有&#xff09;二、PyTorch-CPU1. PyTorch 环境创建2. PyTorch 下载3. Jupyter 中使用 PyTorch三、Python 版本升级与包的维护1. 更新 Anaconda2. 查看与更新 p…...

delphi JSON 数字0.00没有引号解析问题

Delphi中利用SuperObject解析JSON 0.00数字有问题 原因是superObject 的版本问题&#xff1a; 网址&#xff1a;https://www.haolizi.net/example/key_superobject_1.html 下载版本...

log4j2日志输出到控制台-Maven工程

log4j2简介&#xff1a; Apache Log4j 2 is an upgrade to Log4j that provides significant improvements over its predecessor, Log4j 1.x, and provides many of the improvements available in Logback while fixing some inherent problems in Logback’s architecture. …...

【yolov5+deepsort运行和训练自数据集(自看)】

本人在读科研狗&#xff0c;因为研究的是目标跟踪测距等等&#xff0c;所以自己开始百度摸索&#xff0c;中间会遇到很多问题&#xff0c;走很多弯路&#xff0c;往往一个很简单就可以解决的东西都快要把百度用烂&#xff0c;我承认我太菜。本文是留给自己看的&#xff0c;相当…...

word文档怎么在左侧显示目录?

1. 首先&#xff0c;我们使用word打开我们的文档。 2. 打开文档之后&#xff0c;我们向上找到&#xff0c;我们的工具栏&#xff0c;这时word处于“开始”界面 2.1 这里是关键&#xff0c;找到“视图”并点击。 2.2 然后&#xff0c;我们会看到导航窗口&#xff0c;且这里是没…...

淘宝API商品详情接口,通过商品ID获取商品名称,淘宝主图,价格,颜色规格尺寸,库存,SKU等

淘宝的API开发接口&#xff0c;我们需要做下面几件事情。 1&#xff09;开放平台注册开发者账号&#xff1b; 2&#xff09;然后为每个淘宝应用注册一个应用程序键&#xff08;App Key) &#xff1b; 3&#xff09;下载淘宝API的SDK并掌握基本的API基础知识和调用&#xff1b; …...

前端des加密,后端des解密

使用 crypto-js.min.js 的des加密方式加密数据下载地址 https://cdnjs.com/libraries/crypto-js前端加密方式 // 加密方法 function Encrypt(word,skey) {var keyHex CryptoJS.enc.Utf8.parse(skey);let encrypted CryptoJS.AES.encrypt(word, keyHex, {mode: CryptoJS.mod…...

山东曹县人 计算机博士,山东这个小山村火了!走出16名博士、博士后和30多名硕士...

原标题&#xff1a;山东这个小山村火了&#xff01;走出16名博士、博士后和30多名硕士来源&#xff1a;据-紫牛新闻除了近日出圈的“曹县”、免费为适婚青年分配婚房的“南村社区”等网红村居&#xff0c;最近&#xff0c;山东又有一个名叫“刘湖村”的小村庄因为盛产“学霸”走…...

Linux设置定时任务(crontab)

Linux设置定时任务(crontab) 1.crontab命令概述 crontab命令用于设置周期性被执行的指令,并将其存放在/etc/crontab文件,以供之后读取和执行。 cron系统调度进程,可以使用它在每天的非高峰负荷段运行作业,或在一周或一月中不同时段运行,cron是系统主要的调度进程,可以无需人工…...

2021-04-27

命令行技巧 1&#xff09;Tab补全&#xff08;命令与路径&#xff09;、 [rootlocalhost /]# host&#xff08;tab&#xff09;&#xff08;tab&#xff09; #连续按两次Tab&#xff0c;列出以host开头的 host hostid hostname hostnamectl [rootlocal…...

领扣LintCode算法问题答案-614. 二叉树的最长连续子序列 II

领扣LintCode算法问题答案-614. 二叉树的最长连续子序列 II 目录614. 二叉树的最长连续子序列 II描述样例 1&#xff1a;样例 2&#xff1a;题解鸣谢614. 二叉树的最长连续子序列 II 描述 给定一棵二叉树&#xff0c;找到最长连续序列(单调且相邻节点值相差为1)路径的长度(节…...

一、基础知识(3)-共轭函数、次梯度

一、共轭函数 1.1 共轭函数的定义和例子 共轭函数&#xff1a;f∗(y)supx∈domf{yTx−f(x)}f^*(y)\underset{x\in dom f}{sup}\{y^Tx-f(x)\}f∗(y)x∈domfsup​{yTx−f(x)} 几何意义&#xff1a;xyxyxy与f(x)f(x)f(x)之间差值的最大值。 Fenchel 不等式&#xff1a;f(x)f∗(…...

java-接口

接口&#xff0c;和抽象类的概念相似度高&#xff1b; 用关键字interface来修饰&#xff0c;而不是class&#xff1b; public interface 接口名{} 接口的实现需要通过类&#xff0c;类似于子类实现抽象类的实例化&#xff0c;但是类与接口联系起来通过的不是extends继承&…...

【JZ55 二叉树的深度】

描述 输入一棵二叉树&#xff0c;求该树的深度。从根结点到叶结点依次经过的结点&#xff08;含根、叶结点&#xff09;形成树的一条路径&#xff0c;最长路径的长度为树的深度&#xff0c;根节点的深度视为 1 。 数据范围&#xff1a;节点的数量满足 0 ≤ n ≤ 100 &#xf…...

在别墅大宅中打造全屋智能,总共需要几步?

关于智能家居&#xff0c;很多读者可能会想起一些不那么愉快的回忆&#xff1a;2014年左右的智能家居浪潮&#xff0c;涌现出了众多带蓝牙互联功能的家电产品&#xff0c;但数据无法互联互通、单品体验升级有限&#xff0c;加上一些企业竞争失败产品不再更新&#xff0c;留给消…...

SpringBoot项目统一异常处理和统一日志处理

SpringBoot项目统一异常处理和统一日志处理 统一异常处理 本文章介绍三种异常处理:全局异常处理、特定异常处理、自定义异常处理 1、全局异常处理 如果是父子工程,在common子工程中,创建一个子模块common-utils,在子模块下创建一个handler包,在包中创建一个ExceptionHandle…...

Maven下载和安装

第一步&#xff0c;Maven官网下载地址 http://maven.apache.org/download.cgi 第二步&#xff0c;解压文件包 1.apache-maven-3.5.2-bin.zip 直接解压到指定安装路径。 2.apache-maven-3.5.2-src.zip maven源码包。 第三步&#xff0c;配置环境变量&#xff0c;类似…...

天空卫士受邀参加《数据安全治理能力评估方法》团体标准发布会

2021数据安全治理论坛暨《数据安全治理能力评估方法》团体标准发布会5月20日在北京举行。本次论坛由中国互联网协会和中国信息通信研究院&#xff08;以下简称“中国信通院”&#xff09;联合主办&#xff0c;行业专家就数据安全治理标准建设、行业痛点、最佳实践、第三方评估评…...

Spring企业级程序设计 • 【第6章 深入Spring MVC开发】

全部章节 >>>> 本章目录 6.1 模型数据解析及控制器返回值 6.1.1 ModelAndView多种用法 6.1.2 Map添加模型数据和返回String类型值 6.1.3 Model添加模型数据和返回String类型值 6.1.4 返回值为String类型的重定向和转发 6.1.5 实践练习 6.2 Spring MVC表…...

php java bridge phpize,让PHP支持JAVA类

梦想在左&#xff0c;生活在右。 让PHP支持JAVA类恢复服务器服务的时候&#xff0c;广东互联星空那部分php需要调用java类&#xff0c;所以就有了此文。所用软件包及版本j2sdk-1_4_2_05-linux-i586.rpmphp-4.4.4.tar.gzhttpd-2.2.3.tar.gzphp-java-bridge_2.0.8.tar.bz2 (用高…...

RayVentory updated提供硬件和软件的全面清单

RayVentory updated提供硬件和软件的全面清单 RayVentory 提供硬件和软件的全面清单&#xff0c;并提供多种扫描方法&#xff0c;以从日益复杂的 IT 环境中收集最优质的数据。不同数据源(SaaS、PaaS、IaaS 和本地)的集成以及清晰的仪表板和报告的准备作为决策的基础&#xff0c…...

linux12 -MYSQL数据库 -->12日志管理

文章目录mysql的日志日志分类一、mysql错误日志1、配置错误日志&#xff08;默认就是启用的&#xff09;2、在MySQL 5.6中用log_warnings参数3、mysql5.7新增的log_error_verbosity参数二、一般查询日志1、一般查询日志三、二进制日志1、二进制日志简介2、不要混淆以下三种日志…...

超简洁SpringBoot使用AOP统一日志管理

前言 请问今天您便秘了吗&#xff1f;程序员坐久了真的会便秘哦&#xff0c;如果偶然点进了这篇小干货&#xff0c;就麻烦您喝杯水然后去趟厕所一边用左手托起对准嘘嘘&#xff0c;一边用右手滑动手机看完本篇吧。 实现 本篇AOP统一日志管理写法来源于国外知名开源框架JHips…...

单片机按键检测

按键检测步骤&#xff1a; 判断按下 消抖&#xff08;一般为5ms&#xff5e;10ms&#xff09; 等待按键松开 检测松开 if 检测按键的按下&#xff0c; delay 来消抖&#xff0c; while&#xff08;&#xff01;key0&#xff09;&#xff1b; 等待按键松开 。 那么为什么后面要…...

无线网络结构

1.无线网络结构 802.11网络包含四种主要的物理组件&#xff1a;工作站&#xff0c;接入点&#xff0c;无线媒介&#xff0c;分布式系统 802.11规范的重心放在OSI模型的最下面的两层&#xff0c;因为它同时涵盖了物理&#xff08;PHY&#xff09;与数据链路&#xff08;data li…...

Factory CRO与Boston Biomedical Associates宣布合并

此次合并实现了两家公司共同的战略目标&#xff0c;即成为全球领先的医疗器械及医疗技术专攻CRO 荷兰比尔特霍芬和马萨诸塞州马尔堡 -- (美国商业资讯) -- Factory-CRO Group与Boston Biomedical Associates (BBA)宣布两家组织合并&#xff0c;前者是全球领先的合同研究组织(C…...

[Game]留作纪念,为10年后准备

PSV 灵魂献祭SS 通关 打完“集于决战之地的二位神明” 本文写给自己&#xff0c;为十年后自己再玩灵魂献祭SS做准备。 我预计&#xff0c;好多年后我再玩的时候&#xff0c;这个游戏已经跟新游戏没什么区别了。 灵魂献祭 一个非常好的网站 http://wiki.gotvg.com/ss/ind…...

徐有高【TTG多玩自购】PS3《英雄传说 闪之轨迹》

请大家多多支持由在下主力搭建的PS3游戏库( 系统要求4.55&#xff0c;仅供需要的同学下载&#xff0c;不需要的请不要废话&#xff0c;多谢&#xff01;经测试&#xff0c;4.50、4.46系统可以运行&#xff0c;以下系统吃瘪 已转换好的ISO下载&#xff1a; 徐有高 2014-6-24 0…...

干货 | 大数据交易所数据安全流通体系标准化尝试

以下内容整理自清华大学《数智安全与标准化》课程大作业期末报告同学的汇报内容。第一部分&#xff1a;国内大数据交易所发展现状第二部分&#xff1a;国外大数据交易模式及法律法规欧盟的数据交易模式是基于2022年5月16日所提出的《数据治理法案》&#xff0c;其中提出了数据中…...

计算机软件系统有两大部分组成,1、计算机软件系统一般分为(A)两大部分

《1、计算机软件系统一般分为(A)两大部分》由会员分享&#xff0c;可在线阅读&#xff0c;更多相关《1、计算机软件系统一般分为(A)两大部分(5页珍藏版)》请在人人文库网上搜索。1、现代教育技术中心业务知识竞赛题一、选择题&#xff1a;1计算机软件系统一般分为( A )两大部分…...

实验结果包装

如何包装你的实验结果不少研究生们可能都有这样的体会&#xff1a;千辛万苦得来的实验结果&#xff0c;不知道该如何展现给别人&#xff1f;的确如此&#xff0c;有些研究工作做得非常出色&#xff0c;可能由于呈现方式的问题&#xff0c;不能发表高水平的文章&#xff08;尤其…...

如何包装你的实验结果

原文地址在此 不少研究生们可能都有这样的体会&#xff1a;千辛万苦得来的实验结果&#xff0c;不知道该如何展现给别人&#xff1f;的确如此&#xff0c;有些研究工作做得非常出色&#xff0c;可能由于呈现方式的问题&#xff0c;不能发表高水平的文章&#xff08;尤其是SCI文…...

关键词布局-17

1.关键词布局让页面流量暴涨&#xff1a; 口碑和需求—唯品会 用户点击顺序—购买频次和刺激消费欲 价格、季节、销量、折扣、 为什么要买–满足用户需求 为什么要立马买----限时抢购&#xff08;饥饿营销&#xff09;—容易产生口碑效应—占到便宜—提高成交量 怎样买更多—…...

关键词布局

关键词的密度控制&#xff1a;2%-8%之间合理&#xff0c;超过会形成关键词堆砌不足以难以到达理想效果&#xff0c;文章总数除以关键词字数再乘以百分比 关键词堆砌效果&#xff1a;在同一网网页关键词出现多次或类似词语的关键词出现多次即可判断为关键词堆砌&#xff0c; …...

如何设置关键词布局

如何设置关键词布局【199cloud-艾娜】 布局关键词按照首页、栏目页、内页优先等级依次布局。栏目页关键词是首页关键词的扩展&#xff0c;内页长尾词是栏目关键词的扩展。 1、首页关键词 首页关键词是整个网站内容的概括&#xff0c;网站的首页权重高于栏目页和内页&#xff0…...

【百度快速排名】网页要怎么布局关键词才合理?

当前&#xff0c;百度快速排名的实际效果已经远远的超过了普通的网站优化手段。近年来&#xff0c;随着在战神快排方案上越来越有特色之后&#xff0c;基本上都离不开关键词的作用。把关好其中的细节要点&#xff0c;才能够对网站的实际战神快排上起到了很大的帮助。因此&#…...

长尾关键词布局技巧

长尾关键词布局技巧 首先朋友们在基础优化当中都知道网站首页的标题&#xff08;Title&#xff09;和描述&#xff08;Description&#xff09;当中是必须要放上我们网站的核心关键词的&#xff0c;但是由于网站是有每一个网页组合而成&#xff0c;搜索引擎会认为每一个网页都…...

抖音关键词搜索排名优化如何布局操作

抖音短视频平台是一个巨大的流量池&#xff0c;每天拥有五六亿的用户活跃其中。相比于通过图片文字内容引流的品牌&#xff0c;抖音短视频的方式更为粗暴直接&#xff0c;其中的商机也不断被挖掘出来。但不管抖音的玩法再多样&#xff0c;不得不提的是抖音越来越因其搜索功能而…...

关键词布局排名优化方法

网站优化的核心就是关键词排名的优化。做关键词排名的站外因素很大程度上是发外链即锚文本建设利用首页做企业网站目标关键词排名是核心的操作点。 1、关键词布局 关键词是网站优化排名的核心这是毋庸置疑的。因此一定要找出好的关键词并进行一个好的布局。 先把自己行业相关…...

企业网络推广浅析外包企业网络推广如何有效布局关键词优化?

现如今互联网营销推广起势迅猛&#xff0c;尤其是在疫情当下许多企业将营销定位转移到线上营销&#xff0c;这也是众多企业网站着手建设网站优化运营的重要原因之一。当外贸企业将目标瞄准线上营销市场时&#xff0c;由于其定位广泛想要有效实现企业网络推广需要结合有效关键词…...

企业网站建设应该如何布局关键词?

一个网站要做好SEO不外乎就是要做好外部链接的搭建和内部站内的优化。外部链接的搭建主要是做外链、友情链接等&#xff0c;站内优化主要就是关键字布局、内容优化等。接下来今天就详细的说一下怎么进行站内优化中的关键字优化。 网站关键词布局一般分为首页关键词布局、导航栏…...

如何为SEO文章布局关键词已达到更好的排名呢?

众所周知&#xff0c;网站高质量的SEO文章搜索引擎会给予很高的排名与权重。合理安排布局关键词对网站的优化也有非常积极的作用&#xff0c;尤其是对文章内容的更新&#xff0c;网站关键词的作用更为重要了。那么&#xff0c;在编辑文章时如何将关键词合理的布局到文章中&…...

如何对网站关键词进行合理布局?

网站关键词优化是做网站seo优化的至关重要的步骤&#xff0c;如果你网站的关键词布局、关键词运营、关键词设置、关键词挑选没有做好&#xff0c;那样会导致你后期优化效果乏力&#xff0c;不能得到理想的优化成效。所以网站关键词优化不单单只是简单的挑选设置关键词那么简单&…...

关键词布局分析

1、百度快照分析关键字词频关键字词频是指某个页面中出现关键字的次数&#xff0c;从一定侧面上反应了该页面与关键词之间的相关性。关键词词频的把握当然是越多越合理&#xff0c;当然前提是需要了解整个网页结构如何布局&#xff0c;页面中内容丰富度如何&#xff0c;并且关键…...

html 优化关键词 代码,网站关键词SEO优化:HTML代码中应注意的代码

今天&#xff0c;我们将讨论HTML代码中应注意的代码&#xff0c;它更有利于优化搜索引擎&#xff0c;并且某些优化标签的适当布局可能会产生意想不到的结果。一、前端SEO注意1.简化代码&#xff0c;分离并开发CSS&#xff0c;JS和HTML&#xff0c;这更有利于搜索引擎捕获有用的…...

SEO关键词的布局分析

SEO小也最开始的一篇文章《SEO入门一篇就够》中就说到&#xff0c;网站关键词是女神&#xff0c;大家都围着她转&#xff0c;在网站进行排名优化的时候&#xff0c;只有把自己的目标关键词做到首页的位置&#xff0c;再谈客户转化率才有意义&#xff0c;要是网站都没有流量&…...

怎样布局网站关键词最合理

关键词布局对一个网站来说是至关重要的&#xff0c;应按照关键词的热度大小&#xff0c;依次分布在首页、栏目页、内页。首页应放核心关键词&#xff0c;栏目页关键词是首页关键词的扩展&#xff0c;内页长尾词是栏目关键词的扩展。以保定seo&#xff08;www.eoobd.com为例&…...

布局关键词,做好排名的第一步

SEOer推广网站网站目标是什么&#xff1f;无非是网站流量和关键词排名。针对关键词排名的技巧笔者小丹曾分享过很多&#xff0c;而今天我们要分享的就是关键词排名最基础的部分&#xff0c;也是能够为我们快速进行排名提升的部分—关键词布局。布局&#xff0c;之前我们说过网站…...

内容页怎么布局关键词

内容页怎么布局关键词 1、内容页三要素布局关键词 网页的三要素&#xff0c;即title、keyword、description。这三个地方&#xff0c;无论是首页、栏目页还是内容页都要重点布置&#xff0c;尤其是title部分&#xff0c;更不能轻视。关于关键词的布局&#xff0c;可参考文章&am…...

如何合理布局网站关键词

如何合理布局网站关键词&#xff1a;要做好网站优化&#xff0c;懂得合理布局网站关键词很重要。而我们平时写网站优化方案或是开始新站的网站优化&#xff0c;可以先从合理布局网站关键词开始。今天常州seo和大家谈如何合理布局网站关键词。网站每个页面的等级权重不同&#x…...

于飞SEO:seo优化如何正确的布局关键词?

网站需要大量的基础性seo优化设置&#xff0c;关键词布局就是必须进行优化设置的&#xff0c;利用seo中关键词的布局原则&#xff0c;将合适的关键词布局到合适的关键词承载页面&#xff0c;这一块的问题就不大了。 seo怎么布局好关键词这个问题&#xff0c;理论层面看似简单&…...

网站各页面该如何布局关键词优化提升排名?

在网站优化中&#xff0c;最值得关注的一个事情就是关键词的布局&#xff0c;因为关键词的布局直接影响着网站的排名。那么怎样布局关键词才能提高页面和关键词的相关性&#xff0c;并提高网站排名呢&#xff1f;下面一起来看看。一、利用HTML标签布局关键词众所周知&#xff0…...

如何挑选与优化布局关键词

关键词优化是让网站目标关键词在某个搜索引擎上得到更好的排名&#xff0c;让更多的用户都能快速的查找到自己的网站关键词。关键词是优化战略的重要组成部分&#xff0c;我们必须先确认好我们推广的关键词&#xff0c;才能开始做网站的标题与描述等内容的优化&#xff0c;但是…...

如何合理的布局关键词

但我们在做网站推广时&#xff0c;关键词的部署是很关键的&#xff0c;因为搜索引擎在对我们的文章进行抓取的时候&#xff0c;会把文章进行拆分&#xff0c;如果关键词没有合理的布局&#xff0c;我们的文章就会拆的七零八落&#xff0c;对收录是很有影响的。下面就介绍一下该…...

合理布局关键词提升网站权重

如何更合理的布局关键词&#xff0c;以达到良好的站内优化得分&#xff0c;是优化人员的目标之一。 总的来讲&#xff0c;关键词优化技巧有这几点&#xff0c;核心的关键词或者说目标关键词需要布局到首页&#xff0c;次要关键词布局到栏目页或者说列表页&#xff0c;长尾关键词…...

如何挖掘和布局关键词

怎么挖掘关键词-- 目标定位的关键词 网站的目标定位&#xff0c;如一个网站的定位是做 400 电话行业平台的&#xff0c;那么网站 的主关键词就可以是“400 电话办理官网”、“400 电话办理平台”&#xff1b; 网站竞争对手的关键词挖掘&#xff0c;一般只需要打开竞争对手主页的…...

关键词如何布局和优化

对于中小型网站&#xff0c;我们主要是有针对型的对单个关键词或有限的关键词进行优化。那么对于大中型的网站&#xff0c;行业门户站&#xff0c;导航站&#xff0c;商城站&#xff0c;B2B站等这些网站来讲&#xff0c;我们就不是一个两个人来优化&#xff0c;我们要优化的就不…...

关键词的布局

关键词布局的定义&#xff1a; 合理设置关键词的全局分布 通俗的讲&#xff1a;就是关键词出现在哪些位置 F型并不一定完全和字母F一样&#xff0c;以下三种都可以称为F型布局&#xff08;F E&#xff09; 关键词的布局方法&#xff1a; title标题优化keywords关键词优化…...

18.导数的几何意义

...

方向导数、导数、梯度在图形学里的意义

导数是数学概念&#xff0c;对于可导函数&#xff0c;利用割线无限逼近切线&#xff0c;而割线斜率的极线即为切线的斜率&#xff0c;公式为&#xff1a;函数yf(x)在xx0处的导数f′(x0)&#xff0c;表示曲线yf(x)在点P(x0,f(x0))处的切线的斜率k。导数是微积分中的重要基础概念…...

从几何角度理解反函数的导数

从几何角度理解反函数的导数 在同一个函数图像中&#xff0c;反函数和函数表达式是对同一个函数的不同表示 tan⁡(π2−α)tan⁡βcot⁡αtan⁡β1tan⁡αtan⁡β1f′(x)φ′(y)\tan(\frac{\pi}{2}-\alpha)\tan\beta\\ ~\\ \cot\alpha\tan\beta\\ ~\\ \frac{1}{\tan\alpha}\tan…...

吴裕雄--天生自然 高等数学学习:导数的几何意义

转载于:https://www.cnblogs.com/tszr/p/11158684.html...

一阶微分方程的物理意义_微分方程建模课堂讨论之一_利用导数的几何及物理意义建模....

利用导数的几何以及物理意义建模1&#xff0e;将一个手表系在一条链子上&#xff0c;当沿直线拉此链的一端&#xff0c;在平面上&#xff0c;此手表所描述的轨迹是什么&#xff1f;【注】&#xff1a;微积分的发明人莱布尼兹讨论过此问题。【模型】&#xff1a;y(x)-【结果】&a…...

梯度的几何意义

声明在先,困惑了很久了,看来很久很久 目录 梯度求方向导数的几何意义 梯度是什么 梯度垂直于等值面 梯度下降公式...

复数的几何意义

作者&#xff1a;王小龙 链接&#xff1a;https://www.zhihu.com/question/23234701/answer/27293131 来源&#xff1a;知乎 著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 复数不仅有意义&#xff0c;而且可以用图示来优雅地解释。1、实函…...

全纯函数导数的几何意义

设区域$D$上一条曲线$z\gamma(t),a\leq t\leq b$,设起点$\gamma(a)z_{0}$,现有一个定义在$D$上且在$z_{0}$处全纯且$f(z_{0})\neq0$的函数$f(z)$,我们考虑曲线$\gamma$在他映射下的像$$w\sigma(t)f(\gamma(t)),a\leq t\leq b$$ 那么$\sigma(a)f(\gamma(a))\gamma(a)$,因此$${\r…...

方向导数的几何通俗解释

方向导数的几何通俗解释 首先随便给一个曲面 在曲面中随便取两点A&#xff0c;B 当A移动到B时z的值从C变到了D&#xff0c;也就是说随着x&#xff0c;y的变化z的值从C变到了D&#xff0c;我们把CD长度记做Δz 于是Δzf(xΔx,yΔy)-f(x,y) 然后把CD平移到BE&#xff0c;BEΔ…...

导数几何意义应用思维导图【试编辑】

graph TD A[导数的几何意义的应用]-->B[求曲线的切线]; A-->C(求参数的取值范围) subgraph C-->D[其他] end click A callback "Tooltip for a callback" click B "https://www.cnblogs.com/wanghai0666/p/9921940.html" "This is a tooltip…...

导数的几何意义

设曲线yf(x)在点P0处的坐标为(x0,y0)&#xff0c;当自变量由x0变到x0Δx时&#xff0c;点P0沿曲线移动到点P(x0Δx,y0Δy)&#xff0c;直线P0P是曲线yf(x)的割线&#xff0c;其倾角记为φ。 由上图可得&#xff1a; tanφΔyΔx所以&#xff0c;ΔyΔx的几何意义就表示割线P0…...

导数的意义与计算

文章目录一、导数的概念1、导数的物理意义2、导数的几何意义二、导数的计算一、几个常用函数的导数1. 函数yf(x)cyf(x)cyf(x)c 的导数2. 函数yf(x)xyf(x)xyf(x)x 的导数3.函数yf(x)x2yf(x)x^2yf(x)x2 的导数4.函数yf(x)1x的导数yf(x)\frac{1}{x}的导数yf(x)x1​的导数二、导数的…...

工作的同时,我也在这里做副业

文章目录一、什么是独自开&#xff1f;二、独自开能给我们带来什么利益&#xff1f;三、如何使用独自开&#xff1f;3.1、用户任务报价步骤13.2、用户任务报价步骤2四、未来的愿景一、什么是独自开&#xff1f; 独自开&#xff0c;全称独自开发一套系统&#xff0c;是基于商品…...

mysql max min 返回值问题

mysql max min 返回值问题 mysql中&#xff0c;max、min函数&#xff0c;当查询结果没有值时&#xff0c;返回的是null。 所以在java中&#xff0c;若调用了max,min函数&#xff0c;返回结果一定要用Integer接收&#xff0c;而不能是int类型。否则会报错。...

hive的max()、min()等内置计算函数会自动忽略null

select max(id) from table时&#xff0c;如果字段id中存在空值null&#xff0c;或者字段中全部为空值null&#xff0c;sql语法不会报错&#xff0c;前者会忽略null取个最大值&#xff0c;后者直接max出来的结果就是null 在用hive处理数据过程中&#xff0c; max() min() firs…...

使用@Min@Max时添加@NotNull的区别

想要使校验起作用就得在参数头上加上Vaild 否则校验不起作用...

SQL中AVG、COUNT、SUM、MAX等聚合函数对NULL值的处理

一、AVG()求平均值注意AVE()忽略NULL值&#xff0c;而不是将其作为“0”参与计算 二、COUNT() 两种用法 1、COUNT(*)对表中行数进行计数不管是否有NULL 2、COUNT(字段名)对特定列有数据的行进行计数忽略NULL值 三、MAX()、MIN()求最大、最小值都忽略NULL 四、SUM()可以对单个…...

springboot的请求参数约束@Max @Min @NotNull等无效

一句话问题&#xff0c;经验之谈。 经试验&#xff0c;发现可能是 springboot的版本太高。 因为spring-boot-starter-web 在版本2.3之后的的依赖项已经去除了hibernate-validator.6.0.13.Final。 所以会导致注解约束不生效。 要解决这个问题&#xff0c;引入依赖即可。 如&am…...

SpringBoot中的注释使用(@Valid,@Size,@NotNull,@Max)

如下是我写的一段代码&#xff0c;其中会发现if中的条件判断过于繁琐&#xff0c;如果需要更多的属性判断&#xff0c;那代码量难以估量 PostMapping("/admin/category/add")ResponseBodypublic APIRestResponse addCategory(HttpSession session, RequestBody AddC…...

spark对空值null的处理

spark对空值的处理 方法&#xff1a; dff.show() val map Map("age"->30l) dff.na.fill(map).show()对指定的列进行空值填充 这是原本的打印信息&#xff1a; —------ | age| name| —------ |null|Michael| | 30| Andy| | 19| Justin| —------ 这是填充后的…...

cookie.getPath Domain MaxAge 为null的问题

Why Cookie.getDomain() returns null?&#xff1a;https://coderanch.com/t/283519/java/Cookie-getDomain-returns-null javax.servlet.http.Cookie源码中&#xff1a; public Cookie(String name, String value) {if (name null || name.length() 0) {throw new Illegal…...

sql中distinct 的使用及 distinct null 的特殊处理

1.当select语句中包含distinct时&#xff0c;无论遇到多少个空值&#xff0c;结果中只返回一个null...

Java 校验注解@NotNull,@NotBlank,@Max,@Valid等注解简单使用

说明 这些注解多用于进行参数校验,这里挑了几个简单使用下 首先创建一个项目 SpringBoot或者Maven项目都可以,我这里就选择Maven项目了 然后加入依赖 在pom里面添加依赖坐标 <dependency><groupId>org.springframework.boot</groupId><artifactId>…...

mysql max 字符串_SQL中MAX()和MIN()函数的使用(比较字符串的大小)

在SQL数据库中&#xff0c;最大/最小值函数—MAX()/MIN()是经常要用到的&#xff0c;下面就将为您分别介绍MAX()函数和MIN()函数的使用&#xff0c;供您参考&#xff0c;希望对您学习SQL数据库能有些帮助。当需要了解一列中的最大值时&#xff0c;可以使用MAX()函数&#xff1b…...

oracle null 和空字符串

null和’’&#xff08;空字符串&#xff09;是一个意思在使用AVG&#xff0c;MAX&#xff0c;SUM&#xff0c;COUNT等函数时&#xff0c;为NULL的纪录会被忽略。排序时&#xff0c;NULL作为无穷大处理。...

mysql中的null问题

mysql中的null问题 (1) 使用统计函数的时候会出现null值得情况 注意点 sum avg max min 都有可能出现为null的问题 而且 sum avg max min count(具体字段) 都会忽略null的数据 count(*) 不会忽略null数据 (2) 使用count统计需要注意的事项 (3)sum()函数实现统计指定字段值之和…...

java math.max 取数组最大值_Math.max得到数组中最大值

Math.max(param1,param2) 因为参数不支持数组。所以可以根据apply的特点来解决&#xff0c;var max Math.max.apply(null,array)&#xff0c;这样就可以轻易的得到一个数组中最大的一项注&#xff1a;在调用apply的时候第一个参数给了一个null&#xff0c;这个是因为没有对象去…...

Math.max

1、Math.max()函数只能传入一组参数来求最大值&#xff0c;所以如果是要用于求一个数组中的最大值时&#xff0c;可以用Math.max.apply(Math,array),把this值指向Math对象&#xff0c;则第二个参数可以传入任意数组。 2、当给Math.max()或Math.min()函数传参时&#xff0c;若参…...

重构·改善既有代码的设计.02之代码的“坏味道”

前言之前在《重构改善既有代码的设计.01》中初步了解了重构的基本前提&#xff0c;基础原则等入门知识。今天我们继续第二更......识别代码的坏味道Duplicated Code 重复代码。最单纯的Duplicated Code就是“同一个类中含有相同的表达式”或“两个互为兄弟的子类内含有相同表达…...

学习自动化测试有那么重要吗?是不是真的有必要学呢?

你好&#xff0c;我是凡哥。 最近收到不少小伙伴私信提问&#xff0c;其中问得比较多的就是“学习自动化测试有那么重要吗&#xff1f;”。 我的回答是肯定的——很重要。 相信不少同学都有诸如此类的疑问&#xff0c;例如&#xff1a;“日常工作中好像用不上自动化&#xff…...

Linux下的图标与文件关联机制:freedesktop

Linux下的图标与文件关联机制&#xff1a;freedesktop 目前主流的Linux发行版中&#xff0c;使用的桌面基本都是基于GNOME、KDE、Xfce等环境&#xff0c;这几种桌面环境中&#xff0c;关于桌面图标&#xff0c;文件关联&#xff0c;应用程序启动等方面的实现&#xff0c;全部都…...

如何构造 HTTP 请求?

❣️关注专栏&#xff1a; JavaEE 这里写目录标题&#x1f367;1 通过 form 表单构造 HTTP 请求&#x1f368; 1.1 form 发送 GET 请求&#x1f368; 1.2 form 发送 POST 请求&#x1f367; 2 通过 ajax 构造 HTTP 请求&#x1f368; 2.1 js 提供的原生的 ajax 的 API&#x1f…...

数据库:mycat实现读写分离

目录 一、mycat 1、mycat实现读写分离原理 2、mycat应用场景 3、mycat作用 4、mycat实现读写分离实战 一、mycat 1、mycat实现读写分离原理 ①用户进行读操作则由mycat转给配置的从数据库。 ②用户进行写操作则由mycat转给配置的主数据库。 ③转发规则由mycat配置文件中…...

系统测试设计的10种方法

一、等价类划分 等价类的概念 等价类 某个输入域的子集合&#xff0c;在这个集合中 每一个输入条件都是等效 的&#xff0c; 如果其中一个输入不能导致问题发生&#xff0c;那么集合中其它输入条件进行测试也不可能发现错误。有效等价类 合理的输入数据 指满足产品规格说明的…...

js常用方法和内存泄露的几种情况

1. JS数组的常用方法- 增&#xff1a; push() unshift() splice() concat()- 删&#xff1a; pop() shift() splice() slice(不改变原数组)- 改&#xff1a; splice()- 查&#xff1a; indexOf() includes() find()2. 排序- sort() - reverse()3. 转换- join()4. 迭代- forEach…...

DolphinScheduler

序言整理下DolphinScheduler的使用参考资料:https://dolphinscheduler.apache.org/zh-cn/docs/3.1.4 --官网文档简介Apache DolphinScheduler 是一个分布式易扩展的可视化DAG工作流任务调度开源系统。适用于企业级场景&#xff0c;提供了一个可视化操作任务、工作流和全生命周期…...

【Java (一:12-2) 反射】

Java反射机制一、反射1.反射的概念2.获取class对象3. 获取Constructor对象4. 利用Constructor创建对象5.反射获取构造方法流程6.反射获取成员方法流程7.反射获取成员对象8.利用Field赋值和获取值9.获取method对象10.利用method对象运行方法一、反射 1.反射的概念 灵活调用 …...

ESP8266-NodeMCU开发板-------网络知识(2)

目录 TCP/IP协议簇 链路层 网络层与IP协议 IPv4 / IPv6 子网掩码(Subnet Mask) MAC地址和ARP 传输层 应用层 HTTP协议 HTTP请求 HTTP响应 DNS&#xff08;Domain Name System/域名系统&#xff09; TCP/IP协议簇 在网络系统中&#xff0c;为了保证通信设备之间能正确地进行通信…...

设计模式(二十六)----行为型模式之备忘录模式

1 概述 备忘录模式提供了一种状态恢复的实现机制&#xff0c;使得用户可以方便地回到一个特定的历史步骤&#xff0c;当新的状态无效或者存在问题时&#xff0c;可以使用暂时存储起来的备忘录将状态复原&#xff0c;很多软件都提供了撤销&#xff08;Undo&#xff09;操作&…...

前端利用js里数组的filter方法进行多条件过滤查询

需求&#xff1a;用户在输入框输入多个条件时&#xff0c;可以对表格数据进行过滤查询&#xff0c;无需后端接口处理&#xff0c;利用了前端js里数组的filter方法进行过滤。 如下是效果图&#xff1a; 当用户在姓名的输入框里输入"张"后效果如下&#xff1a; 当用户…...

DCDC--开关频率的选择

开关稳压器IC使用的开关频率从数十kHz到数MHz&#xff0c;最近有些甚至似乎以高频率工作。 1、重视效率或重视尺寸 如果将开关频率调高&#xff0c;则外置的电感和电容器将使用较小的&#xff0c;尺寸必然会变小。因此&#xff0c;包含安装面积和高度在内的外形尺寸也会变小&…...

【华为OD机试 2023最新 】 开放日活动、取出尽量少的球(C++)

题目描述 某部门开展Family Day开放日活动,其中有个从桶里取球的游戏,游戏规则如下: 有N个容量一样的小桶等距排开, 且每个小桶都默认装了数量不等的小球, 每个小桶装的小球数量记录在数组 bucketBallNums 中, 游戏开始时,要求所有桶的小球总数不能超过SUM, 如果…...

JavaScript学习笔记(9.6)

JSON JSON是Javascript对象标记法 JSON是一种轻量级的数据交换格式 JSON具有自我描述且易于理解 为什么使用JSON&#xff1a;因为JSON格式仅仅是文本&#xff0c;它能够轻松地在服务器浏览器之间传输&#xff0c;并用作任何编程语言的数据格式。 JavaScript提供内建函数把…...

Profinet协议下,MCGS触摸屏能否无线连接PLC?

一、方案概述 本方案以MCGS触摸屏和2台西门子S7-1200为例&#xff0c;介绍触摸屏与多台 PLC的无线Profinet通信实现过程。在本方案中采用了西门子PLC无线通讯终端DTD418M&#xff0c;作为实现无线通讯的硬件设备。 本方案中&#xff0c;用户无需更改网络参数和原有程序&#…...

小白开发微信小程序20--web api文档制作

1、什么是SwaggerSwagger 项目已于 2015 年捐赠给 OpenAPI 计划&#xff0c;自此它被称为 OpenAPI。 这两个名称可互换使用。 不过&#xff0c;“OpenAPI”指的是规范。 “Swagger”指的是来自使用 OpenAPI 规范的 SmartBear 的开放源代码和商业产品系列。简而言之&#xff1a;…...

审核通过≠报名成功,每年都有朋友因这个细节,报名失败

2023上半年软考已经开放报名啦~ ​ 特别提醒&#xff1a;不是提交报考资料后就算报名成功&#xff01;软考报考流程&#xff1a;提交报考资料→审核通过&#xff08;一般需要1-3个工作日&#xff09;→缴费成功→报名成功&#xff01; ​ ​首次报名软考的同学可能很容易忽…...

进销存是什么?如何选择进销存系统?

什么是进销存&#xff1f;进销存软件概念起源于上世纪80年代&#xff0c;由于电算化的普及&#xff0c;计算机管理的推广&#xff0c;不少企业对于仓库货品的进货&#xff0c;存货&#xff0c;出货管理&#xff0c;有了强烈的需求&#xff0c;进销存软件的发展从此开始。 进入…...

【LeetCode】剑指 Offer 30. 包含min函数的栈 p165 -- Java Version

题目链接&#xff1a;https://leetcode.cn/problems/bao-han-minhan-shu-de-zhan-lcof/ 1. 题目介绍&#xff08;30. 包含min函数的栈&#xff09; 定义栈的数据结构&#xff0c;请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中&#xff0c;调用 min、push 及 p…...

电路基础(4) 电阻电路的一般分析

1.电路的图将上面的电路图&#xff0c;抛开其中元器件的性质&#xff0c;可以提取出“只有线和结点的图”。如果考虑电流等的流向&#xff0c;则可以变化位“有向图”。 提取的有向图少了8那条支路&#xff0c;是因为把元件的并联组合也作为一条支路了。 提取的有向图少了7那条…...

安卓拍照、裁切、选取图片实践

安卓拍照、裁切、选取图片实践 前言 最近项目里面有用到裁切功能&#xff0c;没弄多复杂&#xff0c;就是系统自带的&#xff0c;顺便就总结了一下系统拍照、裁切、选取的使用。网上的资料说实话真是没什么营养&#xff0c;但是Android官网上的说明也有点太简单了&#xff0c…...

tcpdump命令参数说明和使用实例、linux解析公网地址会卡顿一下解决方法

文章目录tcpdumptcpdump的参数选项基本返回值查看基本用法抓取指定网络接口的所有流量抓取指定网络接口指定主机地址【IP/域名】的所有流量只取出端口 3333 的联机数据包获取指定协议的数据包【如udp】在网口eth1上抓取源端口为80且目的端口为6100的数据包进阶用法【关系运算符…...

HBuilderx快捷键大全(2023)

文章目录1. 项目管理器2. 标签卡3. 文件4. 行操作5. 删除6. 列表符操作7. 剪贴板8. 撤销9. 语言10. 包围11. 选择12. 查找13. 跳转14. 视图15. 运行16. 鼠标配合快捷键17. 其他高效极客技巧1. 项目管理器 操作名称快捷键重命名文件&#xff08;焦点在项目管理器中&#xff09;…...

Queue-基于redis的队列

接上文&#xff1a;RBucket对象桶 -&#xff1e; Redisson系列-1&#xff08;让redis操作更优雅&#xff09;_子书少卿的博客-CSDN博客 (Queue) 结构的 RQueue Java对象实现了 java.util.queue 接口。尽管 Roueue 对象无初始大小 (边界)限制&#xff0c;但对象的最大容量受Red…...

prometheus03-如何导出prometheus指标

Prometheus是一个开源的监控系统和时间序列数据库&#xff0c;用于收集和存储服务的指标数据。要导出Prometheus指标&#xff0c;你需要使用或实现一个Prometheus Exporter。以下是一个简单的指南&#xff0c;分为三个主要步骤&#xff1a; 选择或实现Prometheus Exporter Pr…...

Pikachu靶场之XXE漏洞

Pikachu靶场之XXE漏洞一、XML和XXE的区别二、解题1.1 前提1.2 使用代码进行文件读取1.3 内网探针或攻击内网应用1.4 RCE1.5 无回显读取文件1.6 xxe绕过一、XML和XXE的区别 XML是传递数据的一种格式&#xff0c;而XXE是XML传递数据过程中产生的一种漏洞&#xff0c;两者是完全不…...

npm发布包教程(四):迭代

一个npm包发布之后&#xff0c;我们难免会修改一些bug&#xff0c;或者增改一些功能&#xff0c;这就涉及到对npm包的迭代。本篇文章就npm迭代涉及到一些知识点进行介绍。 本次演示以《npm发布包教程&#xff08;二&#xff09;&#xff1a;发布包》中发布的包为基础。 npm包的…...

Apollo 配置变更原理

我们经常用到apollo的两个特性&#xff1a;1.动态更新配置&#xff1a;apollo可以动态更新Value的值&#xff0c;也可以修改environment的值。2.实时监听配置&#xff1a;实现apollo的监听器ConfigChangeListener&#xff0c;通过onChange方法来实时监听配置变化。你知道apollo…...

C语言实现队列(Push Pop Size Front EmptyBack)

队列是一个重要的数据结构&#xff0c;他的特性是先进先出&#xff0c;所以由于这个特性&#xff0c;队列只有一个入口和一个出口&#xff0c;所以只有push和pop 下面我们看一下他如何实现 首先我们来看一下他的结构体 这里我们看到我们定义了两个结构体&#xff0c;其中一个…...

为什么typeof null得到的是Object而不是null?

为什么typeof null得到的是Object而不是null&#xff1f; 因为JavaScript中不同对象在底层都表示为二进制&#xff0c; 而JavaScript中会把二进制前三位都为0的判断成object类型。 而null的二进制表示全是0&#xff0c;自然前三位也是0....

大数据框架之Hive:第12章 企业级调优

第12章 企业级调优 12.1 计算资源配置 本教程的计算环境为Hive on MR。计算资源的调整主要包括Yarn和MR。 12.1.1 Yarn资源配置 1&#xff09;Yarn配置说明 需要调整的Yarn参数均与CPU、内存等资源有关&#xff0c;核心配置参数如下 &#xff08;1&#xff09;yarn.nodem…...

typescript(元组、枚举、类、泛型)

元组 数组合并了相同类型的对象&#xff0c;而元组&#xff08;Tuple&#xff09;合并了不同类型的对象 // 数组 let arr:number[] [1,2] // 元组 let arr1:[string,number][1,2] // 但是使用联合类型/类型别名 同样可以实现元组的效果 // 区别是元组对每一项进行类型约束 …...

独闯万亿氢能江湖,未势能源显露“双轮动能”

在时代的洪流中&#xff0c;顺势而为的企业往往能更快拿到通往未来的船票。如火如荼的新能源行业&#xff0c;无疑是当下较为强劲的“势”。在最近的两会上&#xff0c;国家强调今年要推动发展方式绿色转型&#xff0c;关于加速新能源发展的提案也成为热门议题。市场中不少企业…...

基于opencv的边缘检测方法

1、梯度运算 用OpenCV的形态变换&#xff08; 膨胀、腐蚀、开运算和闭运算&#xff09;函数morphologyEx 梯度运算即膨胀结果-腐蚀结果&#xff1a; 【注意】对于二值图像来说&#xff0c;必须是前景图像为白色&#xff0c;背景为黑色&#xff0c;否则需要进行反二值化处理 …...

RocketMQ支持哪几种类型的消息

一.普通消息 对于普通消息,RocketMQ提供了三种发送方式:同步发送,可靠异步发送和单项发送。 1.同步发送 可靠同步发送指消息发送方发送数据后,在收到接收方的响应后才会发送下一个消息。 2.异步发送 可靠异步发送指发送方发出消息后,不等待接收方响应,接着发送下一个…...

ElasticSearch - SpringBoot整合ES之指定搜索结果返回的字段

文章目录1. 数据准备2. ElasticSearch 搜索结果返回指定的字段3. SpringBoot整合ES 搜索结果返回指定的字段4. 源码接口Elasticsearch的搜索结果可以通过以下参数进行控制&#xff1a;from&#xff1a;指定搜索结果的起始位置&#xff0c;默认为0。 size&#xff1a;指定返回的…...

arcpy基础篇(5)-使用栅格数据

栅格数据是一个独特的空间数据类型。ArcPy中有一个名为arcpy.sa的空间分析模块&#xff0c;该模块将地图代数全部整合到Python环境中&#xff0c;从而提高了脚本运行效率 1.列出栅格要素 ListRaster函数是以Python列表的形式返回工作空间中的栅格要素&#xff0c;该函数语法如…...

2023最全最牛的Jmeter接口测试教程及接口测试详情,你不知道的东西太多了!

下边是详细的jmeter接口测试入门到精通的详细教程&#xff0c;还有视频版本教您实战操作&#xff01; 2023年B站最新Jmeter接口测试实战教程&#xff0c;精通接口自动化测试只需要这一套视频_哔哩哔哩_bilibili2023年B站最新Jmeter接口测试实战教程&#xff0c;精通接口自动化…...

redis 存储一个map 怎么让map中其中一个值设置过期时间,而不是过期掉整个map?

文章目录 redis 存储一个map 怎么让map中其中一个值设置过期时间,而不是过期掉整个map?Java 中 怎么 实现?方案一: Jedis方案二: Lettuce方案三: Redisson方案四: Jedisson方案五: RedisTemplate那种方式 效率最高 ?拓展:结语redis 存储一个map 怎么让map中其中一个值设置过…...

10、CLASSIFIER-FREE DIFFUSION GUIDANCE

简介 论文&#xff1a;https://arxiv.org/pdf/2207.12598.pdf 分类器指导将扩散模型的得分估计与图像分类器的梯度相结合&#xff0c;因此需要训练与扩散模型分开的图像分类器。 实验证明&#xff0c;在没有分类器的情况下&#xff0c;指导确实可以由纯生成模型执行 在无分…...

软件测试金三银四快速入职之如何解决HR已读不会的问题?

目录 前言 一、简历到面试分两个阶段 二、重视简历 三、简历优化 四、工作经历 五、项目经验 六、自我评价荣誉&#xff0c; 前言 金三银四现在正是很多人在面试找工作的时候&#xff0c;很多人在面试的时候都会遇到Hr已读不会的问题。今天就主要来讲一下如何解决下面几个问题&…...

开发人员项目开发的步骤

开发人员项目开发的步骤 前言 项目开发目的分析与确定 在开发商将开发项目确定下来之后&#xff0c;需要与需求方进行讨论&#xff0c;确定需求方对于软件开发需要实现的目标及其具体需要的功能等等&#xff0c;并进行可⾏性分析&#xff08;技术、成本、法律法规&#xff09;…...

Linux 终端、进程组、会话、守护进程

文章目录一、终端概念终端概念控制终端二、进程组概念进程组概述进程组相关 API会话会话概念会话相关 API创建会话注意事项守护进程守护进程介绍守护进程模型守护进程参考代码守护进程相关 API参考文章一、终端概念 终端概念 1、终端&#xff08;Terminal&#xff09; 终端是…...

【MySQL高级篇】第7章_InnoDB数据存储结构

第7章_InnoDB数据存储结构 1. 数据库的存储结构&#xff1a;页 1.1 磁盘与内存交互基本单位&#xff1a;页 1.2 页结构概述 1.3 页的大小 不同的数据库管理系统&#xff08;简称DBMS&#xff09;的页大小不同。比如在 MySQL 的 InnoDB 存储引擎中&#xff0c;默认页的大小是 …...

工具篇 | 10 | 抓包工具

1 前期准备 作为测试开发,为什么要使用抓包工具呢,因在调试过程中,会碰到一些问题,不确定是前端的问题,还是后端的问题,就需要通过一种工具来检测,数据的流转,如web界面或者客户端操作,点击了一个按钮或者浏览一个页面,调用了那些接口,那些参数,请求参数,返回结果…...

Notes04:GPIO功能框图详解

GPIO功能框图详解野火霸道开发板学习笔记信息说明GPIO功能框图输出部分推挽输出推挽输出模型分析推挽输出总结开漏输出开漏输出模型分析开漏输出总结输出数据寄存器(ODR)位设置清除寄存器(BSRR)位清除寄存器(BRR)输入部分输入数据寄存器(IDR)TTL肖特基触发器部分模型分析上拉/下…...

2023系统分析师---系统规划

一、系统规划的步骤 初步调查&#xff1a;根据企业战略目标&#xff0c;分析企业现状以及系统运行状况确定系统目标&#xff1a;确定系统的服务范围质量等分析子系统的组成&#xff1a;系统划分并指定子系统功能拟定系统的实施方案&#xff1a;分析子系统优先级&#xff0c;确…...

Leetcode.1814 统计一个数组中好对子的数目

题目链接 Leetcode.1814 统计一个数组中好对子的数目 Rating &#xff1a; 1738 题目描述 给你一个数组 nums&#xff0c;数组中只包含非负整数。定义 rev(x)的值为将整数 x各个数字位反转得到的结果。比方说 rev(123) 321&#xff0c; rev(120) 21。我们称满足下面条件的下…...

[RK356x Linux] 开发之GPIO使用以及gpio-leds驱动讲解

文章目录一、GPIO 介绍二、RK3568 GPIO 状况三、GPIO 引脚计算四、ITX-3568JQ LED4.1 LED 原理图4.2 LED 设备树4.3 LED 使用五、gpio-leds驱动5.1 介绍5.2 数据结构5.3 驱动分析一、GPIO 介绍 GPIO全称为 General Purpose Input/Output&#xff0c;即通用输入输出端口。它是一…...

5.38 综合案例2.0 -语音助手(短信,蓝牙,M2M设备间通信)

综合案例2.0 - 语音助手案例说明原理器件语音助手功能实现1&#xff0c;ASRPRO-2m模块下载代码连线模块编程说明2&#xff0c;模块与开发板接线3&#xff0c;语音助手代码功能1&#xff1a;语音发短信说明功能2&#xff1a;控制蓝牙设备说明蓝牙灯接线图蓝牙灯代码功能3&#x…...

MySQL与分布式:主从复制

文章目录MySQL与分布式一、Linux下载 MySQL二、主从复制①修改配置文件②配置主机③配置从机④查看同步状态提示&#xff1a;以下是本篇文章正文内容&#xff0c;mysql 系列学习将会持续更新 MySQL与分布式 前面我讲解了 Redis 在分布式场景的下的相关应用&#xff0c;接着我们…...

conda@config命令@package cache包缓存共享@Channels@conda install pyside6

文章目录refs检查基本信息config add pkgs_dirs查看condarc源文件按字段查询全部信息conda Channelconda-forge 通道安装packgeconda install cupy不恰当的url Channel导致的错误Channel指定使用完整的url链接conda installconda临时换源conda install 安装pyside6&#x1f388…...

线性动态规划问题

文章目录1. 三角形中最小路径之和2. 最长递增子序列3. 最长公共子序列1. 三角形中最小路径之和 给定一个三角形 triangle &#xff0c;找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层…...

oracle jdbc 处理块 预处理块

DML DML&#xff08;Data Manipulation Language 数据操控语言&#xff09;用于操作数据库对象中包含的数据&#xff0c;也就是说操 作的单位是记录。 insert 添加记录时需要满足一下条件 类型 长度 兼容: 字段 兼容值 值满足约束 :主键 (唯一非空) 非空(必填) 唯一(不重复…...

关于函数递归调用导致的StackOverflow那点事

身为工程师,Stack Overflow这网站大家应该都是熟到不能再熟。如果没有他帮忙解决各种莫名其妙的错误或者bug,可能连设定个开发环境都要搞半天,更不用说要开发了,产出直接降低好几倍,不如直接下班算了。 但今天要谈的不是那个Stack Overflow,而是要讲程序在使用內存时,因…...

Pandas数据框、序列定义及数据处理应用在线实验闯关

Pandas数据框、序列定义及数据处理应用在线实验闯关 文章目录 Pandas数据框、序列定义及数据处理应用在线实验闯关一、序列和数据框1、任务描述2、相关知识定义列表和元组序列定义方法构造数据框3、任务实现二、外都数据文件读取1、任务描述2、相关知识读取文件分块读取数据3、…...

简介SpringBoot

目录 一、简介SpringBoot 二、SpringBoot项目的创建与使用 1、创建SpringBoot项目 2、使用SpringBoot项目 三、 SpringBoot中的配置文件 .properties配置文件 读取配置文件信息 .yml配置文件 读取配置文件信息 四、SpringBoot中的日志文件 1、日志文件简介 2、…...

【91数据恢复】.[killhackfiles@cock.li].Devos勒索病毒数据恢复

目录 前言&#xff1a;简介 一、什么是.[killhackfilescock.li].Devos勒索病毒&#xff1f; 二、.[killhackfilescock.li].Devos勒索病毒是如何传播感染的&#xff1f; 三、感染了.[killhackfilescock.li].Devos后缀勒索病毒文件怎么恢复&#xff1f; 四、.[killhackfiles…...

TS接口类型

40. TS接口 1. 定义 TypeScript 中的接口是一种抽象结构&#xff0c;用于定义对象的类型。接口定义了对象应该包含的属性和方法&#xff0c;但不提供实现。 TypeScript 的接口类似于其他编程语言中的接口或抽象类&#xff0c;但不同于它们&#xff0c;接口可以描述对象的形状…...