设计模式之抽象工厂模式--创建一系列相关对象的艺术(简单工厂、工厂方法、到抽象工厂的进化过程,类图NS图)

news2024/11/28 15:52:36

目录

  • 概述
    • 概念
    • 适用场景
    • 结构
    • 类图
  • 衍化过程
    • 业务需求
    • 基本的数据访问程序
    • 工厂方法实现数据访问程序
    • 抽象工厂实现数据访问程序
    • 简单工厂改进抽象工厂
    • 使用反射+抽象工厂
    • 反射+配置文件
    • 衍化过程总结
  • 常见问题
  • 总结

概述

概念

    抽象工厂模式是一种创建型设计模式,它提供了一种将相关对象组合在一起创建的方式,而无需指定具体类。该模式通过定义一个抽象工厂接口来创建一系列相关或依赖的对象,而不是直接实例化具体类。这种方式使得系统更加灵活,易于扩展和维护。

适用场景

抽象工厂模式适用于以下情况:

当一个系统需要独立于其产品的创建、组合和表示时;
当一个系统需要由多个系列的产品中的一个进行配置时;
当强调一系列相关产品对象的创建和一起使用时;
当提供一个产品类库,而只想显示它们的接口而不是实现时。

结构

抽象工厂模式包含以下几个角色:

抽象工厂(Abstract Factory):声明了创建一系列产品对象的接口。
具体工厂(Concrete Factory):实现了抽象工厂接口,具体工厂负责创建具体的产品对象。
抽象产品(Abstract Product):声明了具体产品的接口。
具体产品(Concrete Product):实现了抽象产品接口,具体产品是由具体工厂创建的。

类图

在这里插入图片描述

衍化过程

业务需求

    假设有一个简单的应用程序,它使用了 SQL Server 数据库来存储数据。现在需要将数据库更换为 Access 数据库,同时保持应用程序的功能不变。

基本的数据访问程序

在这里插入图片描述
客户端直接和qlserverUser耦合
在这里插入图片描述

//SqlServer
public class SqlServerUser {
    //新增用户
    public void insert(User user){
        System.out.println("在SQlServer中给USER表添加一条数据");
    }
    //获取用户信息
    public User getUser(int id){
        System.out.println("在SQlServer中根据用户id得到USER表中的一条记录");
        return null;
    }
}

//user表
public class User {
    private int _id;
    public int getid(){
        return this._id;
    }
    public void setId(int value){
        this._id=value;
    }
    private String _name;

    public String getname(){
        return this._name;
    }

    public void setname(String value){
        this._name=value;
    }
}

//客户端
public class Client {
    public static void main(String[] args) {
        User user=new User();
        SqlServerUser su=new SqlServerUser();
        su.insert(user);
        su.getUser(1);
    }
}

    在这段客户端代码中,可以看到SqlServerUser su=new SqlServerUser();使su这个对象被框死在了SqlServerUser上。
    现在要做的就是解除客户端和SqlServerUser 的耦合(简答说就是客户端不再依赖SqlServerUser ,那么更换其他的数据库管理系统就不会影响应用程序了)

工厂方法实现数据访问程序

    解除客户端和SqlServerUser对象的耦合,就是把 new SqlServerUser()封装起来,这点上想到使用工厂方法封装new SqlServerUser()过程。
在这里插入图片描述
在这里插入图片描述

抽出两个接口

//工厂接口
interface IFactory {
    public IUser  creatUserDB();

}
//数据库接口
public interface IUser {
    public void insert(User user);
    public User getUser(int id);
}

//SqlServerUser ,用于访问SqlServer的User 
public class SqlServerUser implements IUser {
    //新增用户

    @Override
    public void insert(User user) {
        System.out.println("在SQlServer中给USER表添加一条数据");
    }

    public User getUser(int id){
        System.out.println("在SQlServer中根据用户id得到USER表中的一条记录");
        return null;
    }
}
//SqlServerFactory 实例化SqlServerUser
public class SqlServerFactory implements IFactory {
    @Override
    public IUser creatUser() {
        return new SqlServerUser();
    }
}

//客户端
public static void main(String[] args) {
        Ifactory factory=new SqlServerFactory();
        User user=new User();
        IUser iu = factory.creatUser();
        iu.insert(user);
        iu.getUser(1);
    }

    再来看客户端声明IUser接口的对象iu,事先并不知道要访问哪个数据库,却可以在运行时完成工作,这就是业务逻辑和数据访问的解耦(也就是客户端不再依赖SqlServerUser,如果需要更换AccessUser,只需要创建一个AccessFactory,由AccessFactory封装创建AccessUser对象的过程。)
    现在又有了新的问题:
    数据库里如果不是只有一个User表呢,该怎么办?

抽象工厂实现数据访问程序

    现在需要增加一个Department表,SqlServer和Access分别操作这个Department表,
    再抽出一个IDepartment的接口,两个工厂了分别增加创建SqlserverDepartment和AccessDepartment的方法

在这里插入图片描述
在这里插入图片描述
工厂里两个方法,可以有不同的实现,可以理解为两个不同的系列

//工厂接口
public interface IFactory {
    public IUser creatUserDB();
    public IDepartment createDepartment();
}
//客户端
public class Client {
    public static void main(String[] args) {
        //需要对两个表操作,客户端只认识两个表,不认识Access和SQLServer
        User user = new User();
        Department department = new Department();

        //需要用哪个DBMS就实例化哪个工厂
        IFactory factory = new SqlServerFactory();

        //实例化数据库交给对应的工厂
        IUser iu = factory.creatUserDB();
        //user表里插入,读取操作
        iu.getUser(1);
        iu.insert(user);

        IDepartment idept=factory.createDepartment();
        //department表里插入,读取操作
        idept.getDepartment(2);
        idept.insert(department);
        
    }
}

    再分析一下需求,我们是要读写User表和Department表,IUser和IDepartment里有对应的方法,也就是干活的是这两个接口的实现类,SqlServerUser、AccessUser;以及SqlServerDepartment、AccessDepartment,那谁负责生产这些实现类的对象呢,那就看谁的返回对象是IUser和IDepartment,当然是工厂,SqlServerFactory和AccessFactory.

  • 抽象工厂的优势现在体现出来了:
    1、易于交换产品系列
    由于具体工厂类,例如Factory factory=new AccessFactory0,
    在一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易,它只需要改变具体工厂即可使用不同的产品配置。
    我们的设计不能去防止需求的更改,那么我们的理想便是让改动变得最小
    2、它让具体的创建实例过程与客户端分离(这点工厂方法也做到了)
    客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户代码中。事实上客户端所认识的只有User和Department,至于它是用SQL Server来实现还是Access来实现就不知道了。”

  • 但是仍然存在的问题:
    1、业务扩充
    要增加项目表Project,至少要增加三个类,IProject、.SqlserverProject、AccessProject,.还需要更改IFactory、SqlserverFactory和AccessFactory才可以完全实现。(对应两个表的两个dbms,两个生产dbms的工厂,还有两个接口)
    2、客户端如果多个,改动就很多
    有很多地方使用IUesr或Department,而这样的设计,其实在每一个类的开始都需要声明Factory factory=new SqlserverFactory0,如果有100个调用数据库访问的类,就要更改l00次Factory factory=new AccessFactory()这样的代码

简单工厂改进抽象工厂

    抽象工厂客户端耦合太多
    去掉工厂,创建对象交给DateAccess
在这里插入图片描述
在这里插入图片描述

//DataAccess 负责创建具体数据库对象
public class public class DataAccess {
    private static String db="Sqlserver";//可换成Access
    //创建用户对象工厂
    public static IUser createUser(){
        IUser result=null;
        switch (db){
            case "Sqlserver":
                result=new SqlServerUser();
                break;
            case "Access":
                result=new AccessUser();
                break;
        }
        return result;
    }

    //创建部门对象工厂
    public static  IDepartment createDepartment(){
        IDepartment result=null;
        switch (db){
            case "Sqlserver":
                result=new SqlServerDepartment();
                break;
            case "Access":
                result=new AccessDepartment();
                break;
        }
        return result;
    }

}
{
    private static String db="Sqlserver";//可换成Access
    //创建用户对象工厂
    public static IUser createUser(){
        IUser result=null;
        switch (db){
            case "Sqlserver":
                result=new SqlServerUser();
                break;
            case "Access":
                result=new AccessUser();
                break;
        }
        return result;
    }

    //创建部门对象工厂
    public static  IDepartment createDepartment(){
        IDepartment result=null;
        switch (db){
            case "Sqlserver":
                result=new SqlServerDepartment();
                break;
            case "Access":
                result=new AccessDepartment();
                break;
        }
        return result;
    }

}

//客户端
public class Client {
    public static void main(String[] args) {
        //需要对两个表操作,客户端只认识两个表,不认识Access和SQLServer
        User user = new User();
        Department department = new Department();

        //实例化数据库交给DataAccess
        IUser iu = DataAccess.createUser();
        //user表里插入,读取操作
        iu.getUser(1);
        iu.insert(user);

        IDepartment idept=DataAccess.createDepartment();
        //department表里插入,读取操作
        idept.getDepartment(2);
        idept.insert(department);

    }
}

问题:
    如果需要增加Oracle数据库访问,抽象工厂只增加一个OracleFactory工厂类就可以了,现在就需要在DataAccess类中每个方法的swicth中加case
改进:
    不在程序里写明‘如果是Sqlserver就去实例化SQL Server数据库相关类,如果是Access就去实例化Access相关类’这样的语句,而是根据字符串db的值去某个地方找应该要实例化的类是哪一个。这样就不用switch case了

使用反射+抽象工厂

类图只改变DataAccess就可以
在这里插入图片描述
在这里插入图片描述

public class DataAccess {
    private static String assemblyName="designpatterns.abstractfactory.reflaxAbstractFactoryAccess.DB.";
    private static String db="SqlServer";//数据库名称,可替换为Access
    //创建用户对象工厂
    public static IUser createUser(){
        return (IUser)getInstance(assemblyName+db+"User");
    }
    //创建部门对象工厂
    public static IDepartment createDepartment(){
        return (IDepartment) getInstance(assemblyName+db+"Department");
    }
    private static  Object getInstance(String className){
        Object result=null;
        try {
            result=Class.forName(className).getDeclaredConstructor().newInstance();
        }catch(InvocationTargetException e){
            e.printStackTrace();
        }
        catch(NoSuchMethodException e){
            e.printStackTrace();
        }
        catch(InstantiationException e){
            e.printStackTrace();
        }
        catch(IllegalAccessException e){
            e.printStackTrace();
        }
        catch(ClassNotFoundException e){
            e.printStackTrace();
        }
        return result;
    }


}

    对象可以根据类路径动态创建了,但是在更换数据库访问时,还是需要去改程序(改db这个字符串的值)重编译,如果可以不改程序,那才是真正地符合开放-封闭原则。

反射+配置文件

相对于只用反射,属性db(数据库名)动态获取了
在这里插入图片描述

public class DataAccess {
    private static String assemblyName="designpatterns.abstractfactory.profileReflaxAbstractFactoryAccess.DB.";

 public static String getDb() throws IOException {
   
     String result="";
     
         Properties properties=new Properties();
         properties.load(new FileInputStream("E:\\tgb\\training program\\java\\myProject\\src\\main\\java\\designpatterns\\abstractfactory\\profileReflaxAbstractFactoryAccess\\db.properties"));
         result=properties.getProperty("db");

     return result;
 }

 //创建用户对象工厂
    public static IUser createUser() throws IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
     String db=getDb();
     return (IUser)getInstance(assemblyName+db+"User");
    }
//反射获取对象
    public static  IDepartment createDepartment() throws IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        String db=getDb();
        return (IDepartment) getInstance(assemblyName+db+"Department");
    }
    private static  Object getInstance(String className) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Object result=null;
            result=Class.forName(className).getDeclaredConstructor().newInstance();     
        return result;
    }

}

进步:
    如果更换数据库,无须重新编译代码,只需要改配置文件就可以。
    所有在用简单工厂的地方,都可以考虑用反射技术来去除switch或if,解除分支判断带来的耦合。”
    反射技术可以很好地解决switch 或者if分支难以应对变化,难以维护和扩展的诟病。

衍化过程总结

    在上面的示例中,业务需求是通过数据库连接来执行查询操作。一开始,使用了简单的数据访问方式,直接在应用程序中编写了连接和查询数据库的代码。

    随着业务的发展,需要将数据库从 SQL Server 更换为 Access。为了实现这个数据库更换的需求,采用了工厂方法模式。通过引入抽象产品(IUser)和具体产品(SqlServerUser和AccessUser),抽象工厂(IFactory)和具体工厂(SqlServerFactory和AccessFactory)将数据库连接的创建和使用进行了解耦,使得代码更加灵活和可扩展。

    然后,需要增加一个部门表,让两个数据库去访问,为了应对这种变化,引入了抽象工厂模式。通过在具体工厂(SqlServerFactory和AccessFactory)中增加一个creatDepartment方法,解决了创建SqlServerDepartment和AccessDepartment的问题。
后面又为了更加灵活,介绍了用简单工厂来改进抽象工厂,以及使用反射和配置文件,真正实现三高。

    每次的变化都是为了应对业务需求的不断变化。最初的简单数据访问方式可能适应了当时的需求,但随着业务的发展和技术环境的变化,需要更灵活、可扩展的解决方案。工厂方法和抽象工厂模式提供了一种可扩展的设计,使得我们能够快速适应业务变化,并以相对低的开销进行数据库类型的切换。

常见问题

什么是一系列相关或依赖的对象?
    一系列相关或相互依赖的对象指的是一组具有共同特征或者相互之间存在某种关联关系的对象集合。这些对象通常有着相似或者相互关联的功能、属性或行为。
如何判断这些对象是一系列相关或相互依赖的呢?

  • 共同特征:这些对象具有共同的特征或者属性。例如,它们可能都实现了同一个接口或者继承了同一个抽象类,并拥有类似的方法或属性。

  • 相互关联:这些对象之间存在某种关联关系,彼此之间相互依赖。例如,它们可能是一种逻辑上或功能上相关的对象,需要协同工作以完成某个复杂的任务。

  • 执行步骤:这些对象在特定的操作过程中需要按照一定的顺序或流程进行调用。它们可能是按照特定的顺序被创建、初始化、配置或者销毁的。

  • 统一管理:这些对象可能需要由同一个抽象工厂来创建和管理。抽象工厂可以提供一种统一的方式来创建这些对象,确保它们之间的协调性和一致性。

    总之,一系列相关或相互依赖的对象在抽象工厂模式中是作为一个整体出现的,它们具有共同特征、相互关联,需要按照一定顺序进行操作,并由同一个抽象工厂进行创建和管理。这样可以更好地实现对象之间的协作和组织。
举个栗子
在这里插入图片描述

    比如有一个一个汽车制造厂,可以使用抽象工厂模式来创建一系列相关或相互依赖的对象。

    假设汽车制造厂需要生产不同类型的汽车,如轿车(Sedan)和SUV(Sport Utility Vehicle)。每种类型的汽车由多个部件构成,包括引擎(Engine)、底盘(Chassis)和车身(Body)等。

在这个例子中,可以定义以下抽象类和接口:

  • CarFactory(抽象工厂):定义了用于创建汽车部件的方法。
  • Engine(抽象产品):定义了引擎的功能。
  • Chassis(抽象产品):定义了底盘的功能。
  • Body(抽象产品):定义了车身的功能。

具体的类可以包括:

  • SedanCarFactory(具体工厂):实现了CarFactory接口,并负责创建轿车相关的部件。

  • SedanEngine(具体产品):实现了Engine接口,提供了轿车引擎的具体功能。

  • SedanChassis(具体产品):实现了Chassis接口,提供了轿车底盘的具体功能。

  • SedanBody(具体产品):实现了Body接口,提供了轿车车身的具体功能。

  • SuvCarFactory(具体工厂):实现了CarFactory接口,并负责创建SUV相关的部件。

  • SuvEngine(具体产品):实现了Engine接口,提供了SUV引擎的具体功能。

  • SuvChassis(具体产品):实现了Chassis接口,提供了SUV底盘的具体功能。

  • SuvBody(具体产品):实现了Body接口,提供了SUV车身的具体功能。

    在这个例子中,抽象工厂模式将一系列相关或相互依赖的对象(引擎、底盘和车身)组合到具体的工厂中(轿车工厂和SUV工厂)。每个具体工厂负责创建特定类型汽车所需的部件,并保证这些部件之间的协调性和一致性。

    例如,当需要生产一辆轿车时,可以使用SedanCarFactory创建引擎、底盘和车身。而当需要生产一辆SUV时,可以使用SuvCarFactory创建相应的部件。

    通过使用抽象工厂模式,汽车制造厂可以轻松扩展以生产不同类型的汽车,而无需修改现有代码。同时,不同类型的汽车部件之间的依赖关系也得到了良好的管理和维护,提高了系统的可维护性和可扩展性。

总结

    抽象工厂模式通过引入抽象工厂接口,使得客户端代码与具体产品的实现解耦。它提供了一种简单的方式来创建一系列相关的产品对象,而无需关心具体的实现细节。通过选择不同的具体工厂,可以轻松地切换不同的产品系列,以满足不同的需求。抽象工厂模式在大型系统中非常有用,它能够提高代码的可维护性、可扩展性和可测试性。

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

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

相关文章

rabbitMQ死信队列快速编写记录

文章目录 1.介绍1.1 什么是死信队列1.2 死信队列有什么用 2. 如何编码2.1 架构分析2.2 maven坐标2.3 工具类编写2.4 consumer1编写2.5 consumer2编写2.6 producer编写 3.整合springboot3.1 架构图3.2 maven坐标3.3 构建配置类,创建exchange,queue&#x…

OpenCV查找和绘制轮廓:findContours和drawContours

1 任务描述: 绘制图中粗线矩形的2个边界,并找到其边界的中心线 图1 原始图像 2.函数原型 findContours( InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, …

知识总结 1

传输层重点协议----TCP 1、TCP① TCP协议段格式② TCP原理Ⅰ 确认应答机制(可靠性)Ⅱ 超时重传机制(可靠性)Ⅲ 连接管理机制(可靠性)Ⅳ 滑动窗口机制(效率)Ⅴ 流量控制机制&#xff…

内存函数的介绍和模拟实现

目录 1.memcpy的使用(内存拷贝) 2.memcpy的实现 3.memmove的使用(内存拷贝) 4.memmove的实现 5.memset 的使用(内存设置) 6.memcmp的使用(内存比较) 1.memcpy的使用(内存拷贝) void * memcpy ( void * …

【C++】C++11------线程库

目录 线程库接口线程接口使用lock_guard与unique_lockmutex(互斥锁)lock_guardunique_lock 原子性操作库条件变量(condition_variable) 线程库接口 在C11之前,涉及到多线程问题,都是和平台相关的,比如windows和linux下各有自己的接口&#x…

PMSM——转子位置估算基于QPLL

文章目录 前言仿真模型观测器速度观测位置观测转矩波形电流波形 前言 今后是电机控制方向的研究生的啦,期待有同行互相交流。 仿真模型 观测器 速度观测 位置观测 转矩波形 电流波形

Fake Maxpooling 二维滑动窗口

先对每一行求一遍滑动窗口&#xff0c;列数变为(列数-k1) 再对每一列求一遍滑动窗口&#xff0c;行数变为(行数-k1) 剩下的就是每一个窗口里的最大值啦 #include<bits/stdc.h> #define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); #define endl \nusing nam…

【图论C++】链式前向星(图(树)的存储)

/*** file * author jUicE_g2R(qq:3406291309)————彬(bin-必应)* 一个某双流一大学通信与信息专业大二在读 * * brief 一直在竞赛算法学习的路上* * copyright 2023.9* COPYRIGHT 原创技术笔记&#xff1a;转载需获得博主本人…

idea debug 重启弹窗提示窗口询问是否关闭运行着的服务器

目录 方法121版本的IDEA idea重新启动服务器时会有一个提示窗口询问是否关闭运行着的服务器&#xff0c;&#xff0c;这个窗口不小心点了不再提示.重新打开弹窗方法 方法1 idea编辑器由于勾选了不再提示选项导致的弹窗无法继续弹出&#xff1a;解决方案 1.打开项目没提示&…

Rust 使用Cargo

Rust 使用技巧 Rust 使用crates 假设你正在编写一个 Rust 程序&#xff0c;要使用一个名为 rand 的第三方库来生成随机数。首先&#xff0c;你需要在 Cargo.toml 文件中添加以下依赖项&#xff1a; toml [dependencies] rand "0.7.3" 然后运行 cargo build&…

Mybatis 二级缓存(使用Ehcache作为二级缓存)

上一篇我们介绍了mybatis中二级缓存的使用&#xff0c;本篇我们在此基础上介绍Mybatis中如何使用Ehcache作为二级缓存。 如果您对mybatis中二级缓存的使用不太了解&#xff0c;建议您先进行了解后再阅读本篇&#xff0c;可以参考&#xff1a; Mybatis 二级缓存https://blog.c…

VisionTransformer(ViT)详细架构图

这是原版的架构图&#xff0c;少了很多东西。 这是我根据源码总结出来的详细版 有几点需要说明的&#xff0c;看架构图能看懂就不用看注释了。 &#xff08;1&#xff09;输入图片必须是 224x224x3 的&#xff0c;如果不是就把它缩放到这个尺寸。 &#xff08;2&#xff09;T…

文本嵌入层

1、代码演示 embedding nn.Embedding(10,3) print(embedding) input torch.LongTensor([[1,2,3,4],[4,3,2,9]]) embedding(input) 2、构建Embeddings类来实现文本嵌入层 # 构建Embedding类来实现文本嵌入层 class Embeddings(nn.Module):def __init__(self,d_model,vocab):…

uboot启动流程-涉及_main汇编函数

一. uboot启动流程涉及函数 本文简单分析一下 save_boot_params_ret调用的函数&#xff1a;_main汇编函数。 本文继之前文章的学习&#xff0c;地址如下&#xff1a; uboot启动流程-涉及s_init汇编函数_凌肖战的博客-CSDN博客 二. uboot启动流程涉及的 _main汇编函数 经过之…

微信公众号

title: “微信公众号” createTime: 2022-01-05T10:14:2008:00 updateTime: 2022-01-05T10:14:2008:00 draft: false author: “name” tags: [“杂”] categories: [“software”] description: “测试的” 公众号发布文章 文章目录 title: "微信公众号" createTim…

数据结构与算法基础-(5)---栈的应用-(1)括号匹配

&#x1f308;write in front&#x1f308; &#x1f9f8;大家好&#xff0c;我是Aileen&#x1f9f8;.希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流. &#x1f194;本文由Aileen_0v0&#x1f9f8; 原创 CSDN首发&#x1f412; 如…

UG\NX二次开发 通过点云生成曲面 UF_MODL_create_surf_from_cloud

文章作者:里海 来源网站:《里海NX二次开发3000例专栏》 感谢粉丝订阅 感谢 Rlgun 订阅本专栏,非常感谢。 简介 有网友想做一个通过点云生成曲面的程序,我们也试一下 效果 代码 #include "me.hpp" /*HEAD CREATE_SURF_FROM_CLOUD CCC UFUN */

小谈设计模式(6)—依赖倒转原则

小谈设计模式&#xff08;6&#xff09;—依赖倒转原则 专栏介绍专栏地址专栏介绍 依赖倒转原则核心思想关键点分析abc 优缺点分析优点降低模块间的耦合度提高代码的可扩展性便于进行单元测试 缺点增加代码的复杂性需要额外的设计和开发工作 Java代码实现示例分析 总结 专栏介绍…

python编写修改sqlmap进行_WAF绕过

WAF绕过 文章目录 WAF绕过1 waf机制了解1.1 waf防火墙识别工具1.2 WAF机制及绕过方法总结: [绕waf参考总结地址](https://www.freebuf.com/articles/web/229982.html)1.3 绕过waf&#xff08;安全狗&#xff09;方式 2 绕过分析 -替换格式3 编写py脚本绕过安全狗3.1启动编好的脚…

Bug:elementUI样式不起作用、Vue引入组件报错not found等(Vue+ElementUI问题汇总)

前端问题合集&#xff1a;VueElementUI 1. Vue引用Element-UI时&#xff0c;组件无效果解决方案 前提&#xff1a; 已经安装好elementUI依赖 //安装依赖 npm install element-ui //main.js中导入依赖并在全局中使用 import ElementUI from element-ui Vue.use(ElementUI)如果此…