首页 > 其他分享 >期末考试---设计原则

期末考试---设计原则

时间:2024-11-08 15:16:45浏览次数:3  
标签:String 原则 void class --- 期末考试 println new public

1.里氏替换原则(LSP)

里氏替换原则(Liskov Substitution Principle, LSP)是面向对象设计中的一项重要原则,该原则的核心思想是:

如果对每一个父类对象(基类),都存在一个子类对象(派生类)能够替代它,并且程序的行为没有变化,那么这个子类就是对父类的一个正确的替代。

里氏替换原则的要点
子类应当可以替换父类:在任何使用父类的地方,都可以用子类代替而不影响程序的正确性。
保证行为一致:子类在替换父类时,必须保持父类的行为和期望。也就是说,子类不能改变父类的预期行为。
不改变父类的契约:子类不应引入比父类更严格的前置条件或更宽松的后置条件。即,子类的输入输出和异常处理应当与父类一致。

假设我们在设计一个几何图形系统,其中有一个 Rectangle(矩形)类,并想扩展一个 Square(正方形)类。根据里氏替换原则,Square类应当能够替代 Rectangle 而不改变系统的行为。

// 矩形类
class Rectangle {
    protected int width;
    protected int height;

    public void setWidth(int width) {
        this.width = width;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int getArea() {
        return width * height;
    }
}

违反里氏替换原则的情况

如果我们让 Square 继承自 Rectangle,并尝试通过重写 setWidthsetHeight 来强制保持正方形的性质,那么我们会违反里氏替换原则:

class Square extends Rectangle {
    @Override
    public void setWidth(int width) {
        this.width = width;
        this.height = width; // 保持正方形的性质
    }

    @Override
    public void setHeight(int height) {
        this.width = height;
        this.height = height; // 保持正方形的性质
    }
}
问题

在上述设计中,SquaresetWidthsetHeight 方法修改了矩形的行为,因为在 Rectangle 中,宽和高可以分别设置,但在 Square 中,一旦设置宽或高,另一个也会跟着改变。这种改变违反了 Rectangle 类的基本假设(宽和高是独立的),所以 Square 无法替代 Rectangle 而不引发问题。

Square 替换 Rectangle 的情况下,square.getArea() 的值不符合预期,因为我们期望它遵循矩形的行为,但实际它的宽高被强制相等。

遵循里氏替换原则的改进

要遵循里氏替换原则,我们可以将 RectangleSquare 设计为平行的类,而不是让 Square 继承 Rectangle,比如可以使用一个 Shape 接口或抽象类来表示它们的公共特性:

abstract class Shape {
    public abstract int getArea();
}

class Rectangle extends Shape {
    protected int width;
    protected int height;

    public void setWidth(int width) {
        this.width = width;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    @Override
    public int getArea() {
        return width * height;
    }
}

class Square extends Shape {
    private int side;

    public void setSide(int side) {
        this.side = side;
    }

    @Override
    public int getArea() {
        return side * side;
    }
}

public class TestLSP {
    public static void main(String[] args) {
        Rectangle rectangle = new Rectangle();
        rectangle.setWidth(5);
        rectangle.setHeight(10);
        System.out.println("Rectangle area: " + rectangle.getArea()); // 50

        Square square = new Square();
        square.setSide(5);
        System.out.println("Square area: " + square.getArea()); // 25
    }
}

2. 开放封闭原则(OCP)

开放封闭原则(Open-Closed Principle, OCP)是面向对象设计中的重要原则之一,它的核心理念是:软件实体(如类、模块和函数)应对扩展开放,对修改封闭。这意味着在不修改已有代码的前提下,通过扩展来实现新功能,从而提高系统的灵活性和可维护性。

假设我们正在开发一个订单系统,需要根据不同的支付方式来处理支付逻辑。最初系统只支持信用卡支付,因此我们实现了一个 OrderProcessor 类来处理信用卡支付。

没有遵循开放封闭原则的设计

初始设计时,可能会在 OrderProcessor 类中直接处理不同支付方式的逻辑。代码可能如下:

class OrderProcessor {
    public void processOrder(String paymentType) {
        if (paymentType.equals("creditCard")) {
            System.out.println("Processing credit card payment...");
            // 处理信用卡支付逻辑
        } else if (paymentType.equals("paypal")) {
            System.out.println("Processing PayPal payment...");
            // 处理 PayPal 支付逻辑
        }
        // 未来可能还会加入更多的支付方式
    }
}
问题

这个设计的问题在于,每当我们添加新的支付方式时(如增加 bitcoin 支付),都需要修改 OrderProcessor 的代码,这违反了开放封闭原则。每次修改都可能引入新问题,尤其当逻辑越来越复杂时,维护起来会很困难。

遵循开放封闭原则的设计

为了遵循开放封闭原则,我们可以使用策略模式来重构代码,将每种支付方式的处理逻辑独立到不同的类中。这样,OrderProcessor 不需要知道具体的支付方式,只需要调用支付接口即可。

1.创建支付方式接口

interface Payment {
    void pay();
}

2.为每种支付方式创建具体实现类

class CreditCardPayment implements Payment {
    @Override
    public void pay() {
        System.out.println("Processing credit card payment...");
    }
}

class PayPalPayment implements Payment {
    @Override
    public void pay() {
        System.out.println("Processing PayPal payment...");
    }
}

// 可以轻松添加新的支付方式,比如 Bitcoin 支付
class BitcoinPayment implements Payment {
    @Override
    public void pay() {
        System.out.println("Processing Bitcoin payment...");
    }
}

3.修改 OrderProcessor 使其依赖于接口

class OrderProcessor {
    private Payment payment;

    public OrderProcessor(Payment payment) {
        this.payment = payment;
    }

    public void processOrder() {
        payment.pay();
    }
}

4.使用示例

现在,当我们要处理不同的支付方式时,只需创建相应的支付对象并传递给 OrderProcessor,而无需修改 OrderProcessor 本身的代码:

public class TestOCP {
    public static void main(String[] args) {
        // 使用信用卡支付
        Payment creditCardPayment = new CreditCardPayment();
        OrderProcessor orderProcessor = new OrderProcessor(creditCardPayment);
        orderProcessor.processOrder();

        // 使用 PayPal 支付
        Payment payPalPayment = new PayPalPayment();
        orderProcessor = new OrderProcessor(payPalPayment);
        orderProcessor.processOrder();

        // 新增的 Bitcoin 支付,不需要修改 OrderProcessor
        Payment bitcoinPayment = new BitcoinPayment();
        orderProcessor = new OrderProcessor(bitcoinPayment);
        orderProcessor.processOrder();
    }
}

3. 单一职责原则(SRP)

单一职责原则(Single Responsibility Principle, SRP)强调每个类应该只有一个引起它变化的原因,即一个类只负责一个职责。这意味着一个类只应该负责完成一个功能或逻辑,而不应该承担多个功能,以避免类变得过于复杂,提高代码的可读性和可维护性。

假设我们在开发一个用户管理系统,其中有一个 User 类,负责用户的基本信息管理,同时包含用户的持久化逻辑(如保存到数据库)和发送通知功能。

没有遵循单一职责原则的设计

class User {
    private String name;
    private String email;

    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }

    public String getName() {
        return name;
    }

    public String getEmail() {
        return email;
    }

    // 负责保存用户信息
    public void save() {
        System.out.println("Saving user to the database...");
        // 数据库保存逻辑
    }

    // 负责发送通知
    public void sendNotification() {
        System.out.println("Sending notification to " + email);
        // 通知逻辑
    }
}
问题

在这个设计中,User 类承担了三个职责:

  1. 用户数据的管理(存储用户的 nameemail)。
  2. 持久化(将用户数据保存到数据库)。
  3. 通知功能(给用户发送通知)。

这种设计违反了单一职责原则,带来了一些问题:

  • 如果数据库的保存逻辑发生变化,我们需要修改 User 类。
  • 如果通知方式(例如从邮件通知改为短信通知)发生变化,也要修改 User 类。
  • 类的职责过多,难以理解和维护。

遵循单一职责原则的设计

为了解决上述问题,我们可以将每个职责拆分到单独的类中,使每个类只负责一个职责。

1.User 类只负责管理用户数据

class User {
    private String name;
    private String email;

    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }

    public String getName() {
        return name;
    }

    public String getEmail() {
        return email;
    }
}

2.创建 UserRepository 类,专门负责用户的持久化

class UserRepository {
    public void save(User user) {
        System.out.println("Saving user " + user.getName() + " to the database...");
        // 数据库保存逻辑
    }
}

3.创建 NotificationService 类,专门负责发送通知

class NotificationService {
    public void sendNotification(User user) {
        System.out.println("Sending notification to " + user.getEmail());
        // 通知逻辑
    }
}

4.使用示例

在需要使用这些功能时,我们可以将各个类组合使用:

public class TestSRP {
    public static void main(String[] args) {
        User user = new User("Alice", "[email protected]");

        // 保存用户
        UserRepository userRepository = new UserRepository();
        userRepository.save(user);

        // 发送通知
        NotificationService notificationService = new NotificationService();
        notificationService.sendNotification(user);
    }
}

4. 接口隔离原则(ISP)

接口隔离原则(Interface Segregation Principle, ISP)指出:客户端不应该被强迫依赖于它不使用的方法。也就是说,接口应该小而专,不要让一个接口承担过多的职责,而是将其分解为多个功能单一的接口,让实现类可以选择自己需要实现的接口。

这条原则的目的是避免“胖接口”(拥有过多方法的接口),使得类依赖于最小的接口集合,从而提升系统的灵活性和可维护性。

假设我们在设计一个多功能设备(如多合一的办公设备),这个设备可以提供打印、扫描、传真等功能。我们先来看看没有遵循接口隔离原则的设计。

没有遵循接口隔离原则的设计

interface MultiFunctionDevice {
    void print(Document doc);
    void scan(Document doc);
    void fax(Document doc);
}

假设我们有一些设备实现了 MultiFunctionDevice 接口,但其中有的设备不支持所有功能,比如:

  • 普通打印机只需要 print 功能。
  • 扫描仪只需要 scan 功能。

在这种设计下,即便一个设备只需要部分功能(例如只有打印功能),它也必须实现 MultiFunctionDevice 接口的所有方法。代码可能如下:

class Printer implements MultiFunctionDevice {
    @Override
    public void print(Document doc) {
        System.out.println("Printing document...");
    }

    @Override
    public void scan(Document doc) {
        throw new UnsupportedOperationException("Scan not supported");
    }

    @Override
    public void fax(Document doc) {
        throw new UnsupportedOperationException("Fax not supported");
    }
}
问题

这种设计违反了接口隔离原则,带来了以下问题:

  • 强迫实现无用的方法Printer 类不得不实现 scanfax 方法,即使它不需要这些功能。
  • 增加了代码复杂性:需要额外处理不支持的方法(比如抛出异常),使代码可读性下降。

遵循接口隔离原则的设计

为了解决这个问题,我们可以将 MultiFunctionDevice 接口拆分为多个小接口,每个接口只负责一种功能。这样,类只需实现自己所需的接口,而不必依赖无关的功能。

1.定义多个小接口

interface Printer {
    void print(Document doc);
}

interface Scanner {
    void scan(Document doc);
}

interface Fax {
    void fax(Document doc);
}

2.让具体的设备类实现它们需要的接口

// 只实现打印功能
class SimplePrinter implements Printer {
    @Override
    public void print(Document doc) {
        System.out.println("Printing document...");
    }
}

// 只实现扫描功能
class SimpleScanner implements Scanner {
    @Override
    public void scan(Document doc) {
        System.out.println("Scanning document...");
    }
}

// 实现打印和扫描功能
class MultiFunctionPrinter implements Printer, Scanner {
    @Override
    public void print(Document doc) {
        System.out.println("Printing document...");
    }

    @Override
    public void scan(Document doc) {
        System.out.println("Scanning document...");
    }
}

3.使用示例

不同的设备可以按需组合不同的接口,而不会强迫实现无关的功能:

public class TestISP {
    public static void main(String[] args) {
        Document doc = new Document();

        // 使用简单打印机
        Printer printer = new SimplePrinter();
        printer.print(doc);

        // 使用多功能打印机
        MultiFunctionPrinter multiFunctionPrinter = new MultiFunctionPrinter();
        multiFunctionPrinter.print(doc);
        multiFunctionPrinter.scan(doc);
    }
}

5. 依赖倒置原则(DIP)

依赖倒置原则(Dependency Inversion Principle, DIP)是面向对象设计中的一条重要原则,核心思想是:高层模块不应该依赖于低层模块,两者都应该依赖于抽象;抽象不应该依赖于具体实现,具体实现应该依赖于抽象。简单来说,就是让代码依赖于接口或抽象类,而不是具体的实现类。

目的

依赖倒置原则的目的是降低模块之间的耦合,使得系统更灵活、更易扩展和维护。它使得高层模块(通常是业务逻辑)与低层模块(例如数据存储或第三方服务)的实现细节解耦,因而能够轻松替换或修改底层实现,而不需要更改高层模块的代码。

假设我们有一个简单的消息通知系统,其中 NotificationService 需要依赖发送消息的方式,比如电子邮件或短信。

没有遵循依赖倒置原则的设计

class EmailService {
    public void sendEmail(String message) {
        System.out.println("Sending email: " + message);
    }
}

class NotificationService {
    private EmailService emailService;

    public NotificationService() {
        this.emailService = new EmailService(); // 直接依赖具体的EmailService
    }

    public void sendNotification(String message) {
        emailService.sendEmail(message);
    }
}

在这个设计中:

  • NotificationService 直接依赖于 EmailService,这是一种强耦合关系。
  • 如果我们需要将 EmailService 替换为 SMSService,就必须修改 NotificationService 的代码。
  • 这违反了依赖倒置原则,因为高层模块 NotificationService 依赖于低层模块 EmailService

遵循依赖倒置原则的设计

为了满足依赖倒置原则,我们可以引入一个抽象层,定义一个通用的接口 MessageService,让 NotificationService 依赖于该接口,而不是具体的实现类。然后,我们可以实现不同的 MessageService(例如 EmailServiceSMSService),并通过依赖注入的方式提供具体实现。

1.定义 MessageService 接口

interface MessageService {
    void sendMessage(String message);
}

2.实现 EmailServiceSMSService,它们依赖于 MessageService 接口

class EmailService implements MessageService {
    @Override
    public void sendMessage(String message) {
        System.out.println("Sending email: " + message);
    }
}

class SMSService implements MessageService {
    @Override
    public void sendMessage(String message) {
        System.out.println("Sending SMS: " + message);
    }
}

3.修改 NotificationService,依赖于 MessageService 接口

class NotificationService {
    private MessageService messageService;

    // 通过构造函数注入依赖
    public NotificationService(MessageService messageService) {
        this.messageService = messageService;
    }

    public void sendNotification(String message) {
        messageService.sendMessage(message);
    }
}

4.使用示例

在使用时,可以根据需要将具体实现传递给 NotificationService,实现灵活配置:

public class TestDIP {
    public static void main(String[] args) {
        // 使用EmailService
        MessageService emailService = new EmailService();
        NotificationService notificationService1 = new NotificationService(emailService);
        notificationService1.sendNotification("Hello via Email!");

        // 使用SMSService
        MessageService smsService = new SMSService();
        NotificationService notificationService2 = new NotificationService(smsService);
        notificationService2.sendNotification("Hello via SMS!");
    }
}

6. 合成复用原则(CRP)

合成复用原则(Composite Reuse Principle, CRP)是面向对象设计的一个重要原则。它的核心思想是:优先使用对象组合(Object Composition)而不是继承(Inheritance)来达到代码复用。这一原则也叫做“组合优于继承”(Composition over Inheritance)。

目的

CRP 的目的是通过组合多个对象来扩展类的功能,使得系统更加灵活,减少了继承层次带来的耦合问题。组合比继承更加灵活,因为继承会造成父类和子类之间的强依赖,而组合允许类独立扩展和变化,不需要直接依赖父类的实现。

假设我们要设计一个图形系统,其中有不同的图形(如圆形、矩形等),每种图形可能有不同的绘制方式。

没有遵循合成复用原则的设计

在没有遵循 CRP 的设计中,我们可能会直接使用继承来复用代码,比如将 Shape 作为基类,然后创建 CircleRectangle 类。

// 基础的 Shape 类
class Shape {
    public void draw() {
        System.out.println("Drawing Shape");
    }
}

// Circle 继承 Shape
class Circle extends Shape {
    @Override
    public void draw() {
        System.out.println("Drawing Circle");
    }
}

// Rectangle 继承 Shape
class Rectangle extends Shape {
    @Override
    public void draw() {
        System.out.println("Drawing Rectangle");
    }
}

在这个设计中,CircleRectangle 都继承了 Shape,并且重写了 draw 方法。但是如果我们想要添加更多的行为,比如不同的填充样式或边框样式,继承会导致类层次结构变得复杂,不便于扩展。

遵循合成复用原则的设计

为了满足合成复用原则,我们可以将“图形类型”和“绘制方式”解耦,通过组合的方式来实现不同的绘制方式。具体做法是创建一个 Drawing 接口,每种绘制方式都实现该接口,然后将它组合进 Shape 类中。

1.定义 Drawing 接口

interface Drawing {
    void draw();
}

2.实现不同的绘制方式

class CircleDrawing implements Drawing {
    @Override
    public void draw() {
        System.out.println("Drawing Circle");
    }
}

class RectangleDrawing implements Drawing {
    @Override
    public void draw() {
        System.out.println("Drawing Rectangle");
    }
}

3.通过组合的方式在 Shape 类中使用 Drawing

class Shape {
    private Drawing drawing;

    // 通过构造函数注入绘制方式
    public Shape(Drawing drawing) {
        this.drawing = drawing;
    }

    public void draw() {
        drawing.draw();
    }
}

4.使用示例

在使用时,可以灵活组合不同的 Drawing 实现,而不需要通过继承扩展:

public class TestCRP {
    public static void main(String[] args) {
        // 创建一个圆形绘制方式的 Shape
        Shape circle = new Shape(new CircleDrawing());
        circle.draw();

        // 创建一个矩形绘制方式的 Shape
        Shape rectangle = new Shape(new RectangleDrawing());
        rectangle.draw();
    }
}

7. 迪米特法则(LoD)

迪米特法则(Law of Demeter, LoD),又称最少知识原则(Principle of Least Knowledge),是面向对象设计中的一条重要原则。它的核心思想是:一个对象应当尽可能少地了解其他对象的细节。换句话说,模块之间应当尽量减少相互依赖,这样可以降低系统的耦合性,提高代码的可维护性和灵活性。

规则

迪米特法则主要遵循以下规则:

  1. 只与直接朋友通信:一个对象只应该直接与它需要交互的对象通信,而不应依赖于那些不直接相关的对象。
  2. 不调用陌生对象的属性或方法:避免“链式”调用,例如 a.getB().getC().doSomething(),因为这会让对象依赖于多个不直接相关的对象的实现。

假设我们有一个公司管理系统,Company 包含多个部门(Department),每个部门中包含员工(Employee),公司希望获取各部门的员工姓名。

不遵循迪米特法则的设计

在不遵循迪米特法则的情况下,Company 直接访问 Department 的内部细节,并通过链式调用获取 Employee 的姓名信息:

class Employee {
    private String name;

    public Employee(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

class Department {
    private List<Employee> employees;

    public Department(List<Employee> employees) {
        this.employees = employees;
    }

    public List<Employee> getEmployees() {
        return employees;
    }
}

class Company {
    private List<Department> departments;

    public Company(List<Department> departments) {
        this.departments = departments;
    }

    // 获取所有部门员工姓名
    public List<String> getAllEmployeeNames() {
        List<String> names = new ArrayList<>();
        for (Department dept : departments) {
            for (Employee emp : dept.getEmployees()) { // 直接访问Department内部的员工列表
                names.add(emp.getName());
            }
        }
        return names;
    }
}

在这里,Company 依赖于 Department 的内部结构,并且直接访问 Employee 对象。这种设计违反了迪米特法则,因为 Company 不仅知道 Department 的细节,还依赖 Employee,增加了类之间的耦合性。

遵循迪米特法则的设计

为了遵循迪米特法则,我们可以对 Department 类进行封装,让 Company 只调用 Department 提供的公开方法,而不直接访问 Employee。这样 Company 就不需要知道 Employee 的存在:

class Employee {
    private String name;

    public Employee(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

class Department {
    private List<Employee> employees;

    public Department(List<Employee> employees) {
        this.employees = employees;
    }

    // 提供获取员工姓名的方法
    public List<String> getEmployeeNames() {
        List<String> names = new ArrayList<>();
        for (Employee emp : employees) {
            names.add(emp.getName());
        }
        return names;
    }
}

class Company {
    private List<Department> departments;

    public Company(List<Department> departments) {
        this.departments = departments;
    }

    // 获取所有部门员工姓名
    public List<String> getAllEmployeeNames() {
        List<String> names = new ArrayList<>();
        for (Department dept : departments) {
            names.addAll(dept.getEmployeeNames()); // 只调用Department的公开方法
        }
        return names;
    }
}

在这个设计中:

  • Company 类只调用了 Department 类的 getEmployeeNames 方法,而不需要知道 Department 的内部结构。
  • CompanyEmployee 之间没有直接依赖关系,降低了类之间的耦合性。

标签:String,原则,void,class,---,期末考试,println,new,public
From: https://blog.csdn.net/weixin_54418006/article/details/143528447

相关文章

  • AbMole | MRTX1133(CAS号2621928-55-8;目录号M10593)
    MRTX1133是一种首创的(first-in-class),高度选择性的突变体KRASG12D的抑制剂,可逆地结合激活和失活的KRASG12D突变体并抑制其活性。MRTX1133对KRASG12D的特异性是野生型KRAS的1000倍以上。生物活性MRTX1133是一种有效的、高选择性的KRASG12D抑制剂。MRTX1133......
  • APP压力测试3--Monkey Script命令
    MonkeyScript执行Monkey的脚本命令:adbshellmonkey-f<编写的脚本文件><执行次数>1、DispatchTrackball轨迹球事件轨迹球事件DispatchTrackball(longdowntime(按键最初被按下的时间),longeventide(事件发生的时间),intaction(具体操作了按下还是弹起),floatx(x的坐标点),flo......
  • IconFont - 阿里巴巴矢量图标库:打造高效、美观的网页设计
    文章目录前言一、IconFont概述二、IconFont的主要特点丰富的图标资源强大的图标管理功能灵活的图标编辑选项快速便捷的集成方式活跃的社区交流三、IconFont的使用第一步:注册账号第二步:浏览和选择图标第三步:添加图标到购物车第四步:创建图标库第五步:编辑和管理图标第六步:......
  • upload-labs
    upload-labs1-13upload-labs1-13upload-labs1-13pass-01pass-02pass-03pass-04pass-05pass-06pass-07pass-08pass-09pass-10pass-11pass-12pass-13pass-01首先我们新建文件a.php并用一句话木马<?php@eval($_POST['cmd']);?>然后我们上传1.php发现弹窗给出了白名......
  • VS Code/Code-Runner编译C语言遇到undefined reference to XXX的一种解决办法
    背景用VSCode编译一个C语言编写的项目文件,这个项目除main文件外还有些被引用的C文件,如果不做相关配置的话,运行会报错:即编译时找不到被引用的这些文件,从而报错。解决办法我是使用code-runner这个插件跑的,所以这里只写关于用这种方式运行代码的解决办法。首先查看.vscode/配......
  • Camera List Record - 120
    1.bronicaetrsi##特点120单反,645画幅有135后背,可拍宽幅使用感受酷强:配置全,素质高,质量好实拍2.rolleiflex3.5t特点双镜头反光,对比单反来说体积小,重量轻镜间快门震动小使用感受优雅,太优雅了画质未知实拍待冲洗......
  • Codeforces 909 A-F
    CF909题解题目链接ABCDEF难度:红黄绿蓝绿紫题解A题目翻译:给定两个字符串,求字典序最小的“两字符串非空前缀拼接形成的字符串”。算法标签:贪心题目分析:字典序最小,即从左往右依次比较字符,直到一方不剩字符或两字符不同。因此想到贪心。由于前缀非空,因此在前一字......
  • 国标GB28181公网平台LiteGBS国标GB28181-2022平台,详细介绍LiteGBS视频融合平台的优点
    随着科技的不断进步,视频监控系统在公共安全、智能交通、工业生产及商业管理等领域的应用愈发广泛。构建高效、稳定且标准化的视频监控系统显得尤为关键。视频监控国标平台,即基于GB/T28181协议的视频联网平台,能够对接众多符合国标GB/T28181的设备,如视频平台、NVR录像机、网络监......
  • 三周精通FastAPI:37 包含 WSGI - Flask,Django,Pyramid 以及其它
    官方文档:https://fastapi.tiangolo.com/zh/advanced/wsgi/包含WSGI-Flask,Django,其它¶您可以挂载多个WSGI应用,正如您在 SubApplications-Mounts, BehindaProxy 中所看到的那样。为此,您可以使用 WSGIMiddleware 来包装你的WSGI应用,如:Flask,Django,等等。使......
  • Python从0到100(七十):Python OpenCV-Opencv实现人像迁移
    前言:零基础学Python:Python从0到100最新最全教程。想做这件事情很久了,这次我更新了自己所写过的所有博客,汇集成了Python从0到100,共一百节课,帮助大家一个月时间里从零基础到学习Python基础语法、Python爬虫、Web开发、计算机视觉、机器学习、神经网络以及人工智能相关知......