万字解析设计模式之工厂方法模式与简单工厂模式

一、概述

1.1简介

在java中,万物皆对象,这些对象都需要创建,如果创建的时候直接new该对象,就会对该对象耦合严重,假如我们要更换对象,所有new对象的地方都需要修改一遍,这显然违背了软件设计的开闭原则。如果我们使用工厂来生产对象,我们就只和工厂打交道就可以了,彻底和对象解耦,如果要更换对象,直接在工厂里更换该对象即可,达到了与对象解耦的目的;所以说,工厂模式最大的优点就是:解耦

在本教程中会介绍三种工厂的使用

  • 简单工厂模式(不属于GOF的23种经典设计模式)
  • 工厂方法模式
  • 抽象工厂模式

 1.2简单工厂模式

概述

简单工厂不是一种设计模式,反而比较像是一种编程习惯。

简单工厂模式(Simple Factory Pattern)是一种创建型设计模式,它提供了一个工厂类,该类专门用于创建其他对象。简单工厂模式属于静态工厂模式,即在工厂类中提供静态方法,根据传入的参数不同返回不同的对象实例。

结构

简单工厂模式包含三个角色:

  1. 工厂类(Creator):提供了创建产品的方法,调用者通过该方法来获取产品。
  2. 抽象产品类(Product):定义了产品的规范,描述了产品的主要特性和功能。
  3. 具体产品类(Concrete Product):定义具体的产品实现类,实现抽象产品类中的抽象方法。

 实现

原先逻辑 

 SimpleCoffeeFactory.java

package com.yanyu.SimpleFactory;public class SimpleCoffeeFactory {public Coffee createCoffee(String type) {Coffee coffee = null;if("americano".equals(type)) {coffee = new AmericanoCoffee();} else if("latte".equals(type)) {coffee = new LatteCoffee();}return coffee;}
}

Coffeestore.java

package com.yanyu.SimpleFactory;public class Coffeestore {public Coffee orderCoffee(String type) {Coffee coffee = null;if ("american".equals(type)) {coffee = new AmericanoCoffee();} else if ("latte".equals(type)) {coffee = new LatteCoffee();} else {throw new RuntimeException("对不起,您所点的咖啡没有");}// 加配料coffee.addMilk();coffee.addSugar();return coffee;}
}

Coffee.java

package com.yanyu.SimpleFactory;public abstract class Coffee {public abstract String getName();// 加糖public void addSugar() {System.out.println("加糖");}// 加奶public void addMilk() {System.out.println("加奶");}
}

AmericanoCoffee.java

package com.yanyu.SimpleFactory;public class AmericanoCoffee extends Coffee{public String getName(){return"美式咖啡";}
}

LatteCoffee.java

package com.yanyu.SimpleFactory;public class LatteCoffee extends Coffee{public String getName(){return"拿铁咖啡";}
}

test

package com.yanyu.SimpleFactory;public class Client {public static void main(String[] args) {// 创建咖啡店类对象Coffeestore store = new Coffeestore();Coffee coffee = store.orderCoffee("latte");System.out.println(coffee.getName());}
}

SimpleCoffeeFactory是工厂类,负责生产不同类型的咖啡,Coffeestore是客户端,通过调用工厂类的方法来获取咖啡。简单工厂模式属于创建型模式,通过将对象的创建工作交给工厂类来实现客户端与具体产品类的解耦,可以方便地扩展新的产品类型,同时也便于管理和维护。在这个例子中,Coffeestore只需要调用工厂类的createCoffee方法即可获取不同类型的咖啡,而不需要关心具体如何创建

工厂(factory)处理创建对象的细节,一旦有了SimpleCoffeeFactory,CoffeeStore类中的orderCoffee()就变成此对象的客户,后期如果需要Coffee对象直接从工厂中获取即可。这样也就解除了和Coffee实现类的耦合,同时又产生了新的耦合,CoffeeStore对象和SimpleCoffeeFactory工厂对象的耦合,工厂对象和商品对象的耦合。

后期如果再加新品种的咖啡,我们势必要需求修改SimpleCoffeeFactory的代码,违反了开闭原则。工厂类的客户端可能有很多,比如创建美团外卖等,这样只需要修改工厂类的代码,省去其他的修改操作。

 扩展静态工厂

静态工厂是一种创建对象的方式,它通过一个静态方法来获取对象,有时也被称为静态工厂方法。静态工厂方法通常不需要创建对象,所以可以在不创建对象的情况下直接返回对象。

通过扩展静态工厂,我们可以让工厂方法更加灵活,以便能够生产不同类型的对象。它也不是23种设计模式中的

public class SimpleCoffeeFactory {
​public static Coffee createCoffee(String type) {Coffee coffee = null;if("americano".equals(type)) {coffee = new AmericanoCoffee();} else if("latte".equals(type)) {coffee = new LatteCoffee();}return coffe;}
}

优缺点

优点:

封装了创建对象的过程,可以通过参数直接获取对象。把对象的创建和业务逻辑层分开,这样以后就避免了修改客户代码,如果要实现新产品直接修改工厂类,而不需要在原代码中修改,这样就降低了客户代码修改的可能性,更加容易扩展。

缺点:

增加新产品时还是需要修改工厂类的代码,违背了“开闭原则”。

1.3工厂方法模式

针对上例中的缺点,使用工厂方法模式就可以完美的解决,完全遵循开闭原则。

概述

工厂方法模式(Factory Method Pattern)又称为工厂模式,是一种创建型设计模式。在工厂方法模式中,定义一个用于创建对象的接口,但让子类决定将哪一个类实例化。工厂方法把类的实例化推迟到子类中进行,从而实现了解耦。

结构

工厂方法模式的主要角色:

  • 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
  • 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
  • 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。

 实现

 抽象工厂:

package com.yanyu.FactoryMethod;public interface CoffeeFactory {Coffee createCoffee();
}

具体工厂:

public class LatteCoffeeFactory implements CoffeeFactory {
​public Coffee createCoffee() {return new LatteCoffee();}
}
​
public class AmericanCoffeeFactory implements CoffeeFactory {
​public Coffee createCoffee() {return new AmericanoCoffee();}
}

咖啡店类:

package com.yanyu.FactoryMethod;public class CoffeeStore {private CoffeeFactory factory;public void SetFactory(CoffeeFactory factory) {this.factory = factory;}public Coffee orderCoffee() {Coffee coffee = factory.createCoffee();coffee.addMilk();coffee.addSugar();return coffee;}
}

测试

package com.yanyu.FactoryMethod;public class client {public static void main(String[] args) {// 创建咖啡店对象CoffeeStore store = new CoffeeStore();//创建工厂对象CoffeeFactory factory = new AmericanCoffeeFactory();store.SetFactory(factory);// 点咖啡Coffee coffee = store.orderCoffee();System.out.println(coffee.getName());}
}

这就是实现开闭原则的核心:一旦转换成功,我们拓展功能就只需要增加相应类的or接口即可,不需要再动原来的代码

工厂方法模式是简单工厂模式的进一步抽象。由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。

 优缺点 

优点:

  • 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程;
  • 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则;

缺点:

  • 每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。
  • 工厂方法模式增加了系统抽象性和理解难度,需要对工厂类和具体产品类进行抽象和设计。

 常见应用

常见的使用场景包括:

  1. 数据库连接池的实现。

  2. 日志处理器的实现。

  3. 图形界面控件库的开发。

  4. 任何需要创建对象的场景,特别是对象创建过程复杂或需要隐藏细节的场景。

1.4抽象工厂模式

产品族和同一级别产品

产品族是指一组具有相似特征和用途的产品,它们通常共享相同的品牌名称、市场定位和推广策略。比如,苹果公司的iPhone产品族包括iPhone 12、iPhone 11、iPhone SE 等,它们都是苹果公司的智能手机产品,具有相似的功能和设计。

同一级别产品是指在市场上竞争的,具有相似功能和特点的产品。这些产品可能来自不同的品牌,但是它们都是在同一细分市场上竞争的产品。举例来说,苹果公司的iPhone和三星公司的Galaxy手机都是智能手机,它们在市场上是同一级别的产品。

 概述

在抽象工厂模式中,我们定义一个抽象工厂接口,该接口具有多个工厂方法,每个工厂方法负责创建一个产品族中的一种产品。然后我们创建一个具体的工厂类来实现抽象工厂接口,并在其中实现工厂方法,以便能够创建具体的产品。

抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。

  结构

  • 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法,可以创建多个不同等级的产品。
  • 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
  • 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
  • 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它 同具体工厂之间是多对一的关系。

实现

 抽象工厂:

package com.yanyu.AbstractFactory;public interface DessertFactory {Coffee createCoffee();Dessert createDessert();
}

抽象类

package com.yanyu.AbstractFactory;public abstract class Dessert {public abstract void show();
}

具体工厂:

//美式甜点工厂
public class AmericanDessertFactory implements DessertFactory {
​public Coffee createCoffee() {return new AmericanCoffee();}
​public Dessert createDessert() {return new MatchaMousse();}
}
//意大利风味甜点工厂
public class ItalyDessertFactory implements DessertFactory {
​public Coffee createCoffee() {return new LatteCoffee();}
​public Dessert createDessert() {return new Tiramisu();}
}

具体类:

package com.yanyu.AbstractFactory;public class Trimisu extends Dessert {public void show() {System.out.println("提拉米苏");}
}package com.yanyu.AbstractFactory;public class MatchaMousse extends Dessert {public void show() {System.out.println("抹茶慕斯");}
}

test

package com.yanyu.AbstractFactory;public class Client {public static void main(String[] args) {// 创建的是意大利风味甜品工厂对象// ItalyDessertFactory factory = new ItalyDessertFactory();AmericanDessertFactory factory = new AmericanDessertFactory();// 获取拿铁咖啡和提拉米苏甜品Coffee coffee = factory.createCoffee();Dessert dessert = factory.createDessert();System.out.println(coffee.getName()); dessert.show();
}
}

如果要加同一个产品族的话,只需要再加一个对应的工厂类即可,不需要修改其他的类。

优缺点 

优点:

当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。

缺点:

由于抽象工厂只能创建某个产品族中的全部产品,所以加入新产品时需要修改所有的具体工厂类,增加了维护成本

常见应用

 抽象工厂模式常见应用包括以下几个方面:

1. 操作系统的UI界面设计:操作系统常常提供多种不同的UI界面风格,如Windows提供的经典风格、XP风格、Win7风格等,可以使用抽象工厂模式来实现对不同风格的UI界面进行定制和管理。

2. 游戏开发:游戏中的物品、角色、道具等都可以使用抽象工厂模式来进行设计,不同的游戏可以使用不同的抽象工厂来创建不同的游戏元素。

3. 数据库连接:数据库连接器可以使用抽象工厂模式来设计,通过使用不同的工厂来创建不同类型的数据库连接器,从而支持多种不同的数据库类型。

4. 跨平台开发:不同的操作系统和不同的硬件架构需要不同的代码实现,使用抽象工厂模式可以通过实现不同的工厂来创建适配不同平台的代码。

5. GUI开发:GUI框架常常需要支持多种不同的控件和主题,使用抽象工厂模式可以通过不同的工厂来创建不同的控件和主题。

1.5JDK源码解析-Collection.iterator方法


public class Demo {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("令狐冲");list.add("风清扬");list.add("任我行");
​//获取迭代器对象Iterator<String> it = list.iterator();//使用迭代器遍历while(it.hasNext()) {String ele = it.next();System.out.println(ele);}}
}

 Collection接口是抽象工厂类,ArrayList是具体的工厂类;Iterator接口是抽象商品类,ArrayList类中的Iter内部类是具体的商品类。在具体的工厂类中iterator()方法创建具体的商品类的对象。

 二、实验

简单工厂模式

任务描述

本关任务:某电视机厂专为各知名电视机品牌代工生产各类电视机,当需要海尔牌电视机时只需要在调用该工厂的工厂方法时传入参数“Haier”,需要海信电视机时只需要传入参数“Hisense”,工厂可以根据传入的不同参数返回不同品牌的电视机。现使用简单工厂模式来模拟,程序将会自动从配置文件中读取参数,请根据以下类图来补全代码。

,

相关知识

为了完成本关任务,你需要掌握:

  1. 简单工厂模式包含的角色;
  2. 简单工厂模式缺点。
简单工厂模式包含的角色
  • Factory:工厂角色
  • Product:抽象产品角色
  • ConcreteProduct:具体产品角色
简单工厂模式缺点
  1. 工厂类集中了所有产品的创建逻辑,职责过重,一旦不能正常工作,整个系统都要受到影响;
  2. 增加系统中类的个数(引入了新的工厂类),增加了系统的复杂度和理解难度;
  3. 系统扩展困难,一旦添加新产品不得不修改工厂逻辑;
  4. 由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构,工厂类不能得到很好地扩展。

编程要求

根据提示,给“HaierTV.java,HisenseTV.java,TVFactory.java”三个代码文件内注释需要填空地方补充代码。

Product:抽象产品角色

package step1;public interface TV {void play();
}

ConcreteProduct:具体产品角色

package step1;public class HaierTV implements TV/**填空——————————**/{@Overridepublic void play() {System.out.println("海尔电视机播放中......");}
}
package step1;public class HisenseTV implements TV/**填空——————————**/{@Overridepublic void play() {System.out.println("海信电视机播放中......");}
}

Factory:工厂角色

package step1;public class TVFactory {public static TV produceTV(String brand) throws Exception{if(brand.equalsIgnoreCase("Haier")){System.out.println("电视机工厂生产海尔电视机!");/******填空******/return new HaierTV();/************/}else if(brand.equalsIgnoreCase("Hisense")){System.out.println("电视机工厂生产海信电视机!");/******填空******/return new HisenseTV();/************/}else{throw new Exception("对不起,暂不能生产该品牌电视机!");}}
}

顾客类

package step1;import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;public class XMLUtilTV {public static String getBrandName(){try{//创建文档对象DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();DocumentBuilder builder = dFactory.newDocumentBuilder();Document doc;doc = builder.parse(new File("/data/workspace/myshixun/src/SimpleFactoryconfigTV.xml"));//获取包含品牌名称的文本节点NodeList nl = doc.getElementsByTagName("brandName");Node classNode=nl.item(0).getFirstChild();String brandName=classNode.getNodeValue().trim();return brandName;}catch(Exception e){e.printStackTrace();return null;}}
}

这段代码实现了从指定的XML配置文件中获取电视品牌名称的方法。具体步骤如下:

1. 导入所需要的Java类库,包括org.w3c.dom、javax.xml.parsers和java.io.File。

2. 创建XMLUtilTV类,并定义静态的getBrandName方法,返回类型为String,用于获取配置文件中的品牌名称。

3. 在getBrandName方法中,使用DocumentBuilderFactory类和DocumentBuilder类来创建文档对象,即读取XML配置文件。

4. 获取文档对象中的品牌名称节点,通过doc.getElementsByTagName("brandName")获取节点列表,然后使用nl.item(0)获取第一个节点,再使用getFirstChild()获取节点的第一个子节点。

5. 最后通过classNode.getNodeValue().trim()获取节点的文本值,并去掉开头结尾的空格。

6. 如果有异常发生,打印堆栈信息,并返回null。

package step1;
public class Client {public static void main(String args[]){try{TV tv;String brandName= XMLUtilTV.getBrandName();tv= TVFactory.produceTV(brandName);tv.play();}catch(Exception e){System.out.println(e.getMessage());}}
}

 工厂方法模式

任务描述

本关任务:将原有的电视机工厂进行分割,为每种品牌的电视机提供一个子工厂,海尔工厂专门负责生产海尔电视机,海信工厂专门负责生产海信电视机,如果需要生产 TCL 电视机或创维电视机,只需要对应增加一个新的 TCL 工厂或创维工厂即可,原有的工厂无须做任何修改,使得整个系统具有更加的灵活性和可扩展性。 现使用工厂方法模式来模拟,程序将会自动从配置文件中读取参数,请根据以下类图来修补代码。

,

相关知识

为了完成本关任务,你需要掌握:

  1. 工厂方法模式包含的角色;
  2. 实现要点;
  3. 工厂方法模式缺点。
工厂方法模式包含的角色
  • Product:抽象产品
  • ConcreteProduct:具体产品
  • Factory:抽象工厂
  • ConcreteFactory:具体工厂
实现要点
  1. 让所有产品都遵循同一接口。 该接口必须声明对所有产品都有意义的方法。
  2. 在创建类中添加一个空的工厂方法。 该方法的返回类型必须遵循通用的产品接口。
  3. 在创建者代码中找到对于产品构造函数的所有引用。 将它们依次替换为对于工厂方法的调用, 同时将创建产品的代码移入工厂方法。你可能需要在工厂方法中添加临时参数来控制返回的产品类型。工厂方法的代码看上去可能非常糟糕。 其中可能会有复杂的 switch 分支运算符, 用于选择各种需要实例化的产品类。 但是不要担心, 我们很快就会修复这个问题。
  4. 为工厂方法中的每种产品编写一个创建者子类, 然后在子类中重写工厂方法, 并将基本方法中的相关创建代码移动到工厂方法中。
  5. 如果应用中的产品类型太多, 那么为每个产品创建子类并无太大必要, 这时你也可以在子类中复用基类中的控制参数。
  6. 如果代码经过上述移动后, 基础工厂方法中已经没有任何代码, 你可以将其转变为抽象类。 如果基础工厂方法中还有其他语句, 你可以将其设置为该方法的默认行为。
工厂方法模式缺点
  • 系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,会给系统带来一些额外的开销;
  • 增加了系统的抽象性和理解难度。

编程要求

根据提示,给“HaierTVFactory.java,HisenseTVFactory.java”两个代码文件内注释需要填空地方补充代码。

 Product:抽象产品

package step3;public interface TV {void play();
}

ConcreteProduct:具体产品、

package step3;public class HaierTV implements TV{@Overridepublic void play() {System.out.println("海尔电视机播放中......");}
}
package step3;public class HisenseTV implements TV{@Overridepublic void play() {System.out.println("海信电视机播放中......");}
}

Factory:抽象工厂

package step3;public interface TVFactory {TV produceTV();
}

 ConcreteFactory:具体工厂

package step3;public class HaierTVFactory implements TVFactory{@Overridepublic TV produceTV() {/*填空*/return new HaierTV();}
}
package step3;public class HisenseTVFactory implements TVFactory{@Overridepublic TV produceTV() {/*填空*/return new HisenseTV();}
}

 顾客

package step3;public class Client {public static void main(String args[]){try{TV tv;TVFactory factory;factory=(TVFactory)XMLUtil.getBean();tv=factory.produceTV();tv.play();}catch(Exception e){System.out.println(e.getMessage());}}
}
package step3;import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;public class XMLUtil {public static Object getBean(){try{//创建文档对象DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();DocumentBuilder builder = dFactory.newDocumentBuilder();Document doc;doc = builder.parse(new File("./src/FactoryMethodconfig.xml"));//获取包含类名的文本节点NodeList nl = doc.getElementsByTagName("className");Node classNode=nl.item(0).getFirstChild();String cName=classNode.getNodeValue();//通过类名生成实例对象并将其返回Class c=Class.forName(cName);Object obj=c.newInstance();return obj;}catch(Exception e){e.printStackTrace();return null;}}
}

 

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

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

相关文章

至高直降3000元,微星笔记本双11爆款推荐、好评有礼拿到手软

今年双11来的更早一些&#xff0c;微星笔记本先行的第一波雷影17促销活动&#xff0c;就已经领略到玩家们满满的热情。开门红高潮一触即发&#xff0c;微星笔记本双11活动周期至高直降3000元&#xff0c;众多爆款好货已经开启预约预售&#xff1a;有硬核玩家偏爱的性能双雄&…

接口返回响应,统一封装(ResponseBodyAdvice + Result)(SpringBoot)

需求 接口的返回响应&#xff0c;封装成统一的数据格式&#xff0c;再返回给前端。 依赖 对于SpringBoot项目&#xff0c;接口层基于 SpringWeb&#xff0c;也就是 SpringMVC。 <dependency><groupId>org.springframework.boot</groupId><artifactId&g…

Web APIs——事件流

一、事件流 1.1 事件流与两个阶段说明 事件流指的是事件完整执行过程中的流动路径 说明&#xff1a;假设页面里有个div&#xff0c;当触发事件时&#xff0c;会经历两个阶段&#xff0c;分别是捕获阶段、冒泡阶段 简单来说&#xff1a;捕获阶段是 从父到子 冒泡阶段是从子到父…

震惊! 全方位解释在测试眼里,什么是需求?为什么要有需求?深入理解需求——图文并茂,生活举例,简单好理解

1、什么是需求&#xff1f; 需求定义(官方) 满足用户期望或正式规定文档&#xff08;合同、标准、规范&#xff09;所具有的条件和权能&#xff0c;包含用户需求和软件需求 用户需求&#xff1a;可以简单理解为甲方提出的需求&#xff0c;如果没有甲方&#xff0c;那么就是终端…

查询计算机GUID码

如何查询计算机GUID码&#xff08;全局唯一标识符&#xff09; 1.快键键WINR进入注册表 2.找到\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography路径 3.双击MachineGuid项即可显示计算机GUID码

【网络】序列化反序列化

序列化反序列化 一、序列化反序列化1、概念2、序列化作用3、序列化框架的选择 二、Json1、介绍2、简单使用 一、序列化反序列化 1、概念 在前文《网络编程套接字》中&#xff0c;我们实现了服务器与客户端之间的字符串通信&#xff0c;这是非常简单的通信&#xff0c;在实际使…

Ant Design Vue UI框架的基础使用,及通用后台管理模板的小demo【简单】

一、创建 VUE 项目 npm create vuelatest二、安装使用 ant-design-vue 安装脚手架工具 $ npm install -g vue/cli # OR $ yarn global add vue/cli使用组件 # 安装 $ npm i --save ant-design-vue4.x全局完整注册 import { createApp } from vue; import Antd from ant-de…

Nokogiri库和OpenURI库使用HTTP做一个爬虫

Nokogiri和OpenURI是两个常用的Ruby库&#xff0c;用于编写爬虫程序。它们的主要功能如下&#xff1a; 1、Nokogiri&#xff1a;Nokogiri是一个强大的HTML和XML解析库&#xff0c;可以用于解析网页内容。它提供了一组简单易用的API&#xff0c;可以方便地遍历和操作HTML或XML文…

IOC课程整理-17 Spring事件

1. Java 事件/监听器编程模型 2. 面向接口的事件/监听器设计模式 3. 面向注解的事件/监听器设计模式 4. Spring 标准事件-ApplicationEvent 5. 基于接口的 Spring 事件监听器 6. 基于注解的 Spring 事件监听器 7. 注册 Spring ApplicationListener 8. Spring 事件发布器 9. Spr…

基于VectorGrid加载GeoServer发布的矢量瓦片实例

目录 前言 一、关于VectorGrid 1、开源地址 2、本地示例 二、与LeafLet集成 1、新建html页面 2、地图初始化 3、pbf瓦片地址配置 4、pbf初始化 三、GeoServer跨域问题 1、web.xml配置 2、重启tomcat 总结 前言 回望10月&#xff0c;发生了一些变动&#xff0c;面向未…

LeetCode--196. 删除重复的电子邮箱

文章目录 1 题目描述2 解题思路2.1 代码实现 1 题目描述 表: Person ---------------------- | Column Name | Type | ---------------------- | id | int | | email | varchar | ----------------------id 是该表的主键列(具有唯一值的列)。 该表的每…

OpenCV官方教程中文版 —— Hough 直线变换

OpenCV官方教程中文版 —— Hough 直线变换 前言一、原理二、OpenCV 中的霍夫变换三、Probabilistic Hough Transform 前言 目标 • 理解霍夫变换的概念 • 学习如何在一张图片中检测直线 • 学习函数&#xff1a;cv2.HoughLines()&#xff0c;cv2.HoughLinesP() 一、原理…

贪心算法总结(未完结)

贪心的定义&#xff08;摘自百度百科&#xff09; 贪心算法&#xff08;greedy algorithm&#xff0c;又称贪婪算法&#xff09;是指&#xff0c;在对问题求解时&#xff0c;总是做出在当前看来是最好的选择。也就是说&#xff0c;不从整体最优上加以考虑&#xff0c;算法得到的…

设计模式(2)-创建型模式

1&#xff0c;创建型模式 4.1 单例设计模式 单例模式&#xff08;Singleton Pattern&#xff09;是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式&#xff0c;它提供了一种创建对象的最佳方式。 这种模式涉及到一个单一的类&#xff0c;该类负责创建自己…

初次学习dubbo记录

---------------------------------------10.17---------------------------------------- 集群和分布式概念 集群&#xff1a;很多"人"做的相同的一件事&#xff0c;即使有一个人挂掉了&#xff0c;也不会对系统造成致命影响 分布式&#xff1a;很多"人"…

ruoyi vue前后端分离功能介绍

文章目录 内置功能:用户管理&#xff1a;部门管理&#xff1a;岗位管理&#xff1a;菜单管理&#xff1a;角色管理&#xff1a;字典管理&#xff1a;参数管理&#xff1a; 可以设置是否开启验证码功能通知公告&#xff1a;操作日志&#xff1a;登录日志&#xff1a;在线用户&am…

PAT 乙级1070结绳

题目&#xff1a; 给定一段一段的绳子&#xff0c;你需要把它们串成一条绳。每次串连的时候&#xff0c;是把两段绳子对折&#xff0c;再如下图所示套接在一起。这样得到的绳子又被当成是另一段绳子&#xff0c;可以再次对折去跟另一段绳子串连。每次串连后&#xff0c;原来两…

【ChatGPT系列】ChatGPT:创新工具还是失业威胁?

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…

香港服务器如何做负载均衡?

​  在现代互联网时代&#xff0c;随着网站访问量的不断增加&#xff0c;服务器的负载也越来越重。为了提高网站的性能和可用性&#xff0c;负载均衡成为了一种常见的解决方案。 什么是负载均衡? 负载均衡是一种技术解决方案&#xff0c;用于在多个服务器之间分配负载&#…

【C】想练习C语言?通讯录的实现了解一下

目录 实现思路 开始实现 添加增加联系人功能 添加显示联系人信息的功能 添加删除联系人功能 添加查找指定联系人的功能 添加修改指定联系人的功能 测试 代码 Test.c Contact.c Contact.h 实现思路 1.通讯录中保存人的信息&#xff1a;名字、年龄、性别、电话、住址…