工厂制造细节无需知--工厂方法模式

news2024/10/10 12:21:49

1.1 需要了解工厂制造细节吗?

"简单工厂只是最基本的创建实例相关的设计模式。但真实情况中,有更多复杂的情况需要处理。简单工厂生成实例的类,知道了太多的细节,这就导致这个类很容易出现难维护、灵活性差问题,让人感觉到了不好的味道。"
"知道很多细节不太好吗?"
"现实中,我们要想过好生活,是不太需要也不太可能知道所有细节的。比如说,我们知道猪长什么样子,也知道红烧肉很好吃,但一头猪是通过怎么样的过程变成红烧肉的呢?养殖、运输、屠宰、销售过程的批发、零售,还有饭店或家里的烹饪过程,对我们来说,都是不需要去了解的。我们去饭店吃饭,只要点了红烧肉,过一会儿它就被送出来了,好吃就行了,你说对不对?"


"将来如果能生产出这样一台机器,送进去是猪,出来就是红烧肉,那就好了。"
"嘿嘿!这样的机器我不知道是否生产得出来。不过,整个过程也算是一种封装吧。在我们的程序中,确实存在封装实例创建过程的模式——工厂方法模式,这个模式可以让创建实例的过程封装到工厂类中,避免耦合。它与简单工厂模式是一个体系的,你可以去研究一下。"

1.2 简单工厂模式实现

工厂类是这样写的

package code.chapter1.calculator4;

public class OperationFactory {

    public static Operation createOperate(String operate){
        Operation oper = null;
        switch (operate) {
            case "+":
                oper = new Add();
                break;
            case "-":
                oper = new Sub();
                break;
            case "*":
                oper = new Mul();
                break;
            case "/":
                oper = new Div();
                break;
        }
        return oper;
    }
    
}

客户端是这样的

package code.chapter8.calculator1;

import java.util.Scanner;

public class Test {

	public static void main(String[] args){

		System.out.println("**********************************************");		
		System.out.println("《大话设计模式》代码样例");
		System.out.println();		

		try {
			Scanner sc = new Scanner(System.in);

			System.out.println("请输入数字A:");	
			double numberA = Double.parseDouble(sc.nextLine());
			System.out.println("请选择运算符号(+、-、*、/):");	
			String strOperate = sc.nextLine();
			System.out.println("请输入数字B:");	
			double numberB = Double.parseDouble(sc.nextLine());
			
			Operation oper = OperationFactory.createOperate(strOperate);
			
			double result = oper.getResult(numberA,numberB);

			System.out.println("结果是:"+result);	
		}
		catch(Exception e){
			System.out.println("您的输入有错:"+e.toString());	
		}

		System.out.println();
		System.out.println("**********************************************");

	}
}

1.3 工厂方法模式实现

先建一个工厂接口:

package code.chapter8.calculator1;

public interface IFactory {

    public Operation createOperation();
    
}
package code.chapter8.calculator1;

//加法工厂
public class AddFactory implements IFactory {

    public Operation createOperation(){
        return new Add();
    }
    
}
package code.chapter8.calculator1;

//乘法工厂
public class MulFactory implements IFactory {

    public Operation createOperation(){
        return new Mul();
    }
    
}
package code.chapter8.calculator1;

//减法工厂
public class SubFactory implements IFactory {

    public Operation createOperation(){
        return new Sub();
    }
    
}
package code.chapter8.calculator1;

//除法工厂
public class DivFactory implements IFactory {

    public Operation createOperation(){
        return new Div();
    }
    
}
package code.chapter8.calculator1;

public class OperationFactory {

    public static Operation createOperate(String operate){
        Operation oper = null;
        IFactory factory = null;
        switch (operate) {
            case "+":
                factory = new AddFactory();
                break;
            case "-":
                factory = new SubFactory();
                break;
            case "*":
                factory = new MulFactory();
                break;
            case "/":
                factory = new DivFactory();
                break;
        }
        oper = factory.createOperation();
                
        return oper;
    }
    
}

1.4 简单工厂vs.工厂方法

"怪就怪在这里呀,以前我们不是说过,如果我现在需要增加其他运算,比如求x的n次方(xn),或者求a为底数b的对数(logab),这些功能的增加,在简单工厂里,我是先去加求x的n次方的指数运算类,然后去更改OperationFactory类,当中加'Case'语句来做判断。现在用了工厂方法,加指数运算类没问题,去改OperationFactory类的分支也没问题,但又增加了一个指数工厂类,这不等于不但没有降低难度,反而增加类,把复杂性增加了吗?为什么要这样?"
"问得好。简单工厂模式的最大优点在于工厂类中包含必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。就像你的计算器,让客户端不用管该用哪个类的实例,只需要把'+'给工厂,工厂自动就给出了相应的实例,客户端只要去做运算就可以了,不同的实例会实现不同的运算。但问题也就在这里,如你所说,如果要加一个'求x的n次方(xn)'的功能,我们需要给OperationFactory类的方法里加'Case'的分支条件。目前来看,这个OperationFactory类,承载了太多功能,这可不是好办法。这就等于说,我们不但对扩展开放了,对修改也开放了,这样就违背了什么原则?"
"哦,是的,违背的是开放-封闭原则。"
"对,也就是说,我们加减乘除运算的部分已经相当成熟了,但是因为增加新的功能,就要去改已经很成熟的类代码,这就好比很多鸡蛋放在了一个篮子里,这是很危险的。"
"那么工厂方法模式,就可以解决这个问题吗?我感觉我本来是4个运算类,1个工厂类,共5个类,现在多出了4个运算工厂类和1个工厂接口,问题依然没有解决。"
"哈哈,那是因为你没有真的理解工厂方法。举一个例子,我们公司本来只有一家工厂,生产四种不同的产品。后来发展得特别好,需要增加新的两种产品放在另一个地方开设新的工厂。新的工厂不应该影响原有工厂的正常工作,你说怎么办?"
"新工厂建在别的地方,应该不影响原有的工厂运作,最多就是建好后,总公司那里再增加一些协调管理部门就好了。"
"说得非常好。就编程来说,我们应该尽量将长的代码分派切割成小段,再将每一小段'封装'起来,减少每段代码之间的耦合,这样风险就分散了,需要修改或扩展的难度就降低了。加减乘除四个类算是一个工厂的产品,不妨叫它们(基础运算工厂)类,现在增加指数、对数运算类,如果算是另一种工厂的两种产品,不妨称它为'高级运算工厂'类,你觉得有必要去影响原有的基础运算工厂运作吗?"
"哦!我明白你的意思了。并不是要去创建加法工厂、减法工厂这样的类,而是将加减乘除用一个基础工厂来创建,现在增加了新的产品,又不想影响原有的工厂代码,于是就扩展一个新的工厂来处理。我马上改。"
"下面是原有的工厂结构,加减乘除运算已经非常稳定,尽量不要去改变它们。"

"增加了一种新的工厂和两种新的运算类(以后可以扩展更多的高级运算,比如正余弦、正余切等),不要影响原有的代码和运作。"

"增加两个运算类。"

package code.chapter8.calculator2;

//指数运算类,求numberA的numberB次方
public class Pow extends Operation {

    public double getResult(double numberA, double numberB){
        //此处缺两参数的有效性检测
        return Math.pow(numberA,numberB);
    }
    
}


package code.chapter8.calculator2;

//对数运算类,求以numberA为底的numberB的对数
public class Log extends Operation {

    public double getResult(double numberA, double numberB){
        //此处缺两参数的有效性检测
        return Math.log(numberB)/Math.log(numberA);
    }
    
}


工厂接口不变:

package code.chapter8.calculator2;

public interface IFactory {

    public Operation createOperation(String operType);
    
}

"基础运算工厂类,此类已经比较成熟稳定,实现后应该封装到位,不建议轻易修改。"

package code.chapter8.calculator2;

//基础运算工厂
public class FactoryBasic implements IFactory {

    public Operation createOperation(String operType){
        Operation oper = null;
        switch (operType) {
            case "+":
                oper = new Add();
                break;
            case "-":
                oper = new Sub();
                break;
            case "*":
                oper = new Mul();
                break;
            case "/":
                oper = new Div();
                break;
        }
                
        return oper;
    }
    
}


"高级运算工厂类,也许还有扩展产品的可能。"

package code.chapter8.calculator2;

//高级运算工厂
public class FactoryAdvanced implements IFactory {

    public Operation createOperation(String operType){
        Operation oper = null;
        switch (operType) {
            case "pow":
                oper = new Pow();//指数运算类实例
                break;
            case "log":
                oper = new Log();//对数运算类实例
                break;

            //此处可扩展其他高级运算类的实例化,但修改
            //当前工厂类不会影响到基础运算工厂类

        }
                
        return oper;
    }    
}





"左侧新的OperationFactory类与右侧原来的OperationFactory类对比。"

package code.chapter8.calculator2;

public class OperationFactory {

    public static Operation createOperate(String operate){
        Operation oper = null;
        IFactory factory = null;
        switch (operate) {
            case "+":
            case "-":
            case "*":
            case "/":
                //基础运算工厂实例
                factory=new FactoryBasic();
                break;
            case "pow":
            case "log":
                //高级运算工厂实例
                factory=new FactoryAdvanced();
                break;
        }  
        //利用多态返回实际的运算类实例
        oper = factory.createOperation(operate);
        return oper;
    }
}













"你或许会发现,新的Opera口与具体工厂类,并不存在具体的实现,与原来的OperationFactory类对比,实例化的过程延迟到了工厂子类中。"不过新的OperationFactory类依然存在'坏味道',当增加新的运算子类时,它本身也是需要更改的,这个先放在一边,以后可以解决。"
"Perfect!我明白了。这就是前面提到的针对接口编程,不要对实现编程吧?"
"是的。我们来看工厂方法的定义。注意关键词——延迟到子类。"

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

工厂方法模式(Factory Method)结构图

package code.chapter8.factorymethod;

public class Test {

	public static void main(String[] args){

		System.out.println("**********************************************");		
		System.out.println("《大话设计模式》代码样例");
		System.out.println();		

		Creator[] creators = new Creator[2];
		creators[0] = new ConcreteCreatorA();
		creators[1] = new ConcreteCreatorB();

		for(Creator item : creators){
			Product product = item.factoryMethod();
			product.make();
		}

		System.out.println();
		System.out.println("**********************************************");

	}
}

//Product类
abstract class Product {
	public abstract void make();
}

//ConcreteProductA类
class ConcreteProductA extends Product {
	public void make(){
		System.out.println("产品A制造");
	}
}

//ConcreteProductB类
class ConcreteProductB extends Product {
	public void make(){
		System.out.println("产品B制造");
	}
}

//Creator类
abstract class Creator {
	public abstract Product factoryMethod();
}

class ConcreteCreatorA extends Creator{
	public Product factoryMethod(){
		return new ConcreteProductA();
	}
}

class ConcreteCreatorB extends Creator{
	public Product factoryMethod(){
		return new ConcreteProductB();
	}
}



"我们讲过,既然这个工厂类与分支耦合,那么我就对它下手,根据依赖倒转原则,我们把工厂类抽象出一个接口,这个接口只有一个方法,就是创建抽象产品的工厂方法。然后,所有的要生产具体类的工厂,就去实现这个接口,这样,一个简单工厂模式的工厂类,变成了一个工厂抽象接口具体生成对象的工厂。每个工厂可以有多个不同产品,而工厂之间,又是相对隔离封装状态。这样过去已经比较完善的工厂和产品体系,就不需要再去改动它们,而另外需要变更的代码,完全可以通过扩展来变化,这就完全符合了开放-封闭原则的精神。"
"哦,工厂方法从这个角度讲,的确要比简单工厂模式来得强。"
"严格来说,是一种升级。当只有一个工厂时,就是简单工作模式,当有多个工厂时,就是工厂方法模式。类似由一维进化成了二维,更强大了。"

1.5 商场收银程序再再升级

package code.chapter6.decorator5;

public class CashContext {

    private ISale cs;   //声明一个ISale接口对象

    //通过构造方法,传入具体的收费策略
    public CashContext(int cashType){
        switch(cashType){
            case 1:
                this.cs = new CashNormal();
                break;
            case 2:
                this.cs = new CashRebate(0.8d);
                break;
            case 3:
                this.cs = new CashRebate(0.7d);
                break;
            case 4:
                this.cs = new CashReturn(300d,100d);
                break;
            case 5:
                //先打8折,再满300返100
                CashNormal cn = new CashNormal();
                CashReturn cr1 = new CashReturn(300d,100d); 
                CashRebate cr2 = new CashRebate(0.8d);
                cr1.decorate(cn);   //用满300返100算法包装基本的原价算法
                cr2.decorate(cr1);  //打8折算法装饰满300返100算法
                this.cs = cr2;      //将包装好的算法组合引用传递给cs对象
                break;
            case 6:
                //先满200返50,再打7折
                CashNormal cn2 = new CashNormal();
                CashRebate cr3 = new CashRebate(0.7d);
                CashReturn cr4 = new CashReturn(200d,50d); 
                cr3.decorate(cn2);  //用打7折算法包装基本的原价算法
                cr4.decorate(cr3);  //满200返50算法装饰打7折算法
                this.cs = cr4;      //将包装好的算法组合引用传递给cs对象
                break;
        }
    }

    public double getResult(double price,int num){
        //根据收费策略的不同,获得计算结果
        return this.cs.acceptCash(price,num);
    }    
}

"以前不觉得,现在发现确实是太多的new实例了,尤其是5和6,装饰模式的使用,在这个CashContext类中,显得特别的复杂。"

创建几个工厂类来处理这些new?

"从上面的代码来看,我感觉至少应该有原价销售类、打折类、满减返利类、先打折再满减类和先满减再打折类,一共五个工厂类。"

"尝试抽象一下,能不能合并一部分呢?"

"我想想。好像原价类,可以想象成打折的参数为1的打折类。但打折与满减返利好像合并不了。"

"它俩是合并不了,但先打折后满减类能不能涵盖它们俩?"

"我懂了。如果有'先打折后满减类'存在,那它应该有三个初始化参数:折扣值、满减条件、满减返利值,那么打折类,其实就是满减返利值条件为0的情况,另外满减类,就相当于折扣参数为1的情况。"那就只需要'先打折再满减'和'先满减再打折'两个工厂类了。"

1.6 简单工厂+策略+装饰+工厂方法

"我的实现方法,首先原有的ISale、CashSuper、CashNormal、CashReturn、CashRebate等类都不变。"

"增加IFactory接口。"

package code.chapter8.factorymethod1;

public interface IFactory {

   public ISale createSalesModel(); //创建销售模式

}



"增加实现IFactory接口的两个类,'先打折再满减'类和'先满减再打折'类,其中红框部分代码为装饰模式的实现。"

package code.chapter8.factorymethod1;

//先打折再满减类
public class CashRebateReturnFactory implements IFactory {
    
    private double moneyRebate = 1d;
    private double moneyCondition = 0d;
    private double moneyReturn = 0d;

    public CashRebateReturnFactory(double moneyRebate,double moneyCondition,double moneyReturn){
      this.moneyRebate=moneyRebate;
      this.moneyCondition=moneyCondition;
      this.moneyReturn=moneyReturn;
    }

    //先打x折,再满m返n
    public ISale createSalesModel(){
        
        CashNormal cn = new CashNormal();
        CashReturn cr1 = new CashReturn(this.moneyCondition,this.moneyReturn); 
        CashRebate cr2 = new CashRebate(this.moneyRebate);
        
        cr1.decorate(cn);   //用满m返n算法包装基本的原价算法
        cr2.decorate(cr1);  //打x折算法装饰满m返n算法
        return cr2;         //将包装好的算法组合返回
    }    
}


package code.chapter8.factorymethod1;

//先满减再打折类
public class CashReturnRebateFactory implements IFactory {
    
    private double moneyRebate = 1d;
    private double moneyCondition = 0d;
    private double moneyReturn = 0d;

    public CashReturnRebateFactory(double moneyRebate,double moneyCondition,double moneyReturn){
      this.moneyRebate=moneyRebate;
      this.moneyCondition=moneyCondition;
      this.moneyReturn=moneyReturn;
    }

    //先满m返n,再打x折
    public ISale createSalesModel(){
        
        CashNormal cn2 = new CashNormal();
        CashRebate cr3 = new CashRebate(this.moneyRebate);
        CashReturn cr4 = new CashReturn(this.moneyCondition,this.moneyReturn); 

        cr3.decorate(cn2);  //用打x折算法包装基本的原价算法
        cr4.decorate(cr3);  //满m返n算法装饰打x折算法
        return cr4;         //将包装好的算法组合返回
    }    
}


"有了上面的这些准备后,CashContext类就简单多了,它针对的是ISale接口、IFactory接口编程,然后两个工厂类,对于各个打折满减算法CashSuper、CashNormal、CashReturn、CashRebate等具体类一无所知。实现了松耦合的目的。"

package code.chapter8.factorymethod1;

public class CashContext {
    private ISale cs;   //声明一个ISale接口对象
    //通过构造方法,传入具体的收费策略
    public CashContext(int cashType){
        IFactory fs=null;
        switch(cashType) {
            case 1://原价
                fs = new CashRebateReturnFactory(1d,0d,0d);
                break;
            case 2://打8折
                fs = new CashRebateReturnFactory(0.8d,0d,0d);
                break;
            case 3://打7折
                fs = new CashRebateReturnFactory(0.7d,0d,0d);
                break;
            case 4://满300返100
                fs = new CashRebateReturnFactory(1,300d,100d);
                break;
            case 5://先打8折,再满300返100
                fs = new CashRebateReturnFactory(0.8d,300d,100d);
                break;
            case 6://先满200返50,再打7折
                fs = new CashReturnRebateFactory(0.7d,200d,50d);
                break;
        }
        this.cs = fs.createSalesModel();
    }

    public double getResult(double price,int num){
        //根据收费策略的不同,获得计算结果
        return this.cs.acceptCash(price,num);
    }    
}


"我感觉工厂方法克服了简单工厂违背开放-封闭原则的缺点,又保持了封装对象创建过程的优点。"
大鸟:"说得好,它们都是集中封装了对象的创建,使得要更换对象时,不需要做大的改动就可实现,降低了客户程序与产品对象的耦合。工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。就像生活中,凡是在基层工作过的人都知道,具体事情做得越多,越容易犯错误。相反,如果做官做得高了,说出的话就会比较抽象、笼统,很多时候犯错误的可能性反而就越来越小了。"
"工厂方法模式是不是本质就是对获取对象过程的抽象?"
"说得非常对,就是这样。工厂方法的好处有这么几条:第一,对于复杂的参数的构造对象,可以很好地对外层屏蔽代码的复杂性,注意是指创建新实例的构造对象。比如说我们用了'先打折再满减'类工厂,其实就屏蔽了装饰模式的一部分代码,让CashContext不再需要了解装饰的过程。第二,很好的解耦能力。这点刚才你也说了,这就是针对接口在编程。当我们要修改具体实现层的代码时,上层代码完全不了解实现层的情况,因此并不会影响到上层代码的调用,这就达到了解耦的目的。"
"对了。你说这还不是最佳的做法?那应该如何做呢?还有就是这样还是没有避免修改客户端的代码呀?"
"哈,之前我就提到过,利用'反射'可以解决避免分支判断的问题。不过今天还是不急,等以后再谈。"
"好的好的。你饿了吗?我们要不去撸串吃点夜宵?"
"走!去吃封装羊肉去。"

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

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

相关文章

python--IO流和字符流的写入写出

1.IO流:(input output stream) python的IO流只有一个函数:open函数 属性不用带括号;方法通通要带括号 输入输出流:狭义上来说,指的就是内存数据和磁盘这种可以永久 存储数据的设备 IO流 IO流…

LongAdder 和 Striped64 基础学习

cs,表示 Cell 数组的引用;b,表示获取的 base 值,类似于 AtomicLong 中全局变量的 value 值,在没有竞争的情况下数据直接累加到 base 上,或者扩容时,也需要将数据写入到 base 上;v&am…

计算机考研408有向无环图描述表达式可靠构造方法

目录 前言目标(以王道书为例)构造方法1. 建树2. 后序遍历1. a2. b3. 4. b5. c6. d7. 8. *9. *10. c 前言 对王道视频中的分层合并思想不是很满意,笔者提出自己的构造方法。 目标(以王道书为例) 构造方法 笔者通过王…

GitGithub小册:版本管理必备利器

在 GitHub上创建仓库 这一步骤的前提是先注册一个 GitHub的账号,由于那都是小场面,就不再巴拉巴拉。 说明一下,全文如果没有特别的单独说明,文中所有用【】括起来的内容都代表网页或者软件节点的操作按钮或者步骤选择项。 打开 Gi…

【QT入门】 自定义标题栏界面qss美化+按钮功能实现

往期回顾: 【QT入门】 鼠标按下和移动事件实现无边框窗口拖动-CSDN博客【QT入门】 设计实现无边框窗口拉伸的公用类-CSDN博客【QT入门】对无边框窗口自定义标题栏并实现拖动和拉伸效果-CSDN博客 【QT入门】 自定义标题栏界面qss美化按钮功能实现 一、最终效果 二、…

AI论文速读 |【综述】 时序分析基础模型:教程与综述

论文标题:Foundation Models for Time Series Analysis: A Tutorial and Survey 作者: Yuxuan Liang(梁宇轩), Haomin Wen(温浩珉), Yuqi Nie(PatchTST一作), Yushan Jiang, Ming J…

redis的常用基本命令与持久化

文章目录 redis的基本命令1.授权密码2.增加、覆盖、查询、删除、切换库名、移动、清空数据库 Redis持久化RDB模式主动备份自动备份RDB备份过程 AOF备份模式开启AOF备份模式执行流程 总结 redis的基本命令 1.授权密码 config set requirepass 密码设置完密码需要认证密码以后才…

最简单的 H.264 视频码流解析程序

最简单的 H.264 视频码流解析程序 最简单的 H.264 视频码流解析程序原理源程序运行结果下载链接参考 最简单的 H.264 视频码流解析程序 参考雷霄骅博士的文章:视音频数据处理入门:H.264视频码流解析 本文中的程序是一个H.264码流解析程序。该程序可以从…

​IAA+IAP混合变现趋势下,出海手游广告玩法解析 | TopOn出海干货

3月23日,TopOn 携手罗斯基及汇量科技旗下一站式跨渠道智能投放工具XMP联合主办的“2023游戏出海新机会”上海站线下沙龙成功举办。 本次活动邀请到多位业内知名公司、平台的负责人,分别从海外投放增长策略、产品融合玩法方向、商业混合变现模式、三方安…

C++万物起源:类与对象(三)拷贝构造、赋值重载

目录 一、拷贝构造函数 1.1拷贝构造函数的概念与特征 1.2拷贝构造的实现 1.3默认构造函数 1.4拷贝构造函数典型调用场景 二、赋值运算符重载 2.1赋值运算符重载的格式 一、拷贝构造函数 1.1拷贝构造函数的概念与特征 在c语言语法中,我们可以将一个变量赋值给…

OSPF中配置静态路由备份 实验简述

OSPF中配置静态路由备份 实验简述 静态路由备份是一种网络路由的备份机制,用于提高网络的可靠性和冗余性。而静态路由备份则是在主路由失效时,自动切换到备用路由,以确保网络的连通性。 主路由默认优先级为60,备份路由设置优先级1…

Doris实践——信贷系统日志分析场景的实践应用

目录 前言 一、早期架构演进 1.1 架构1.0 基于Kettle MySQL离线数仓 1.2 架构2.0 基于 Presto / Trino统一查询 二、基于Doris的新一代架构 三、新数仓架构搭建经验 3.1 并发查询加速 3.2 数仓底座建设 四、Doris助力信DolphinScheduler 和 Shell 贷业务场景落地 4.…

前端学习<四>JavaScript基础——03-常量和变量

常量(字面量):数字和字符串 常量也称之为“字面量”,是固定值,不可改变。看见什么,它就是什么。 常量有下面这几种: 数字常量(数值常量) 字符串常量 布尔常量 自定义…

C#编写MQTT客户端软件

主要参考C#MQTT编程06--MQTT服务器和客户端(winform版)_c#mqttserver-CSDN博客 但由于使用的.NET版本和MQTT库版本存在差异,因此有些不同。 MQTT协议内容在此不做描述,仅介绍VS使用C#的实现过程。本次使用VS2015,.netframwork4.6。 C#语言本身…

从零到一:基于 K3s 快速搭建本地化 kubeflow AI 机器学习平台

背景 Kubeflow 是一种开源的 Kubernetes 原生框架,可用于开发、管理和运行机器学习工作负载,支持诸如 PyTorch、TensorFlow 等众多优秀的机器学习框架,本文介绍如何在 Mac 上搭建本地化的 kubeflow 机器学习平台。 注意:本文以 …

STM32单片机智能电表交流电压电流程序设计(电流 电压互感器TV1005M+TA1005M)

资料下载地址:STM32单片机智能电表交流电压电流程序设计(电流 电压互感器TV1005MTA1005M) 1、摘要 5、基于STM32F103单片机智能电表交流电压电流设计 本设计由STM32单片机核心板电路交流电压电流检测模块电路WIFI模块电路指示灯电路组成。 1、通过电压互感器TV100…

【Canavs与艺术】绘制蓝白绶带大卫之星勋章

【图例】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>用Canvas绘制蓝白绶带大卫之星勋章</title><style type&quo…

计算机网络—TCP协议详解:特性、应用(2)

&#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 ♈️今日夜电波&#xff1a;マリンブルーの庭園—ずっと真夜中でいいのに。 0:34━━━━━━️&#x1f49f;──────── 3:34 &#x1f504; ◀️…

我愿把这个网站成为全球最强AI网站!弄100多个AI伺候你??

家人们&#xff0c;你们猜我发现了什么牛逼的AI网站&#xff1f;&#xff1f; 直接上图&#xff1a; 这个网站&#xff0c;聚合了国内外100多个顶尖的AI&#xff0c;包括了OpenAI家的GPT3.5、GPT4、GPT4V、GPT4.5系列&#xff0c;以及Anthropic家的Claude3 Opus、Claude3 Sone…

Spark-Scala语言实战(11)

在之前的文章中&#xff0c;我们学习了如何在spark中使用RDD中的cartesian,subtract最终两种方法。想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请留下你宝贵的点赞&#xff0c;谢谢。 Spark-Scal…