【设计模式】建造者模式 Builder Pattern
前面学习了简单工厂模式,工厂方法模式以及抽象工厂模式,这些都是创建类的对象所使用的一些常用的方法和套路, 那么如果我们创建一个很复杂的对象可上面的三种方法都不太适合,那么“专业的事交给专业人去做”,23设计模式总有一个模式是适合这种复杂对象的创建。比如现在的智能手机组成, 它包括一个屏幕,摄像头,耳机接口,USB接口,CPU, RAM,主板等等, 但是每一个型号的手机的屏幕又不一样,有的是刘海的,有的是全屏的,有的是全面屏的,CUP 也不一样,有骁龙820 的,有 660的还有麒麟920 的等等,手机的组成图如下:
那么要创建一个这样的复杂对象, 该怎么创建呢? 那么该建造者模式闪亮登场了。
一、建造者模式定义
建造者模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式是一种对象创建型模式。
二、建造者模式结构图
1、AbstractBuilder(抽象建造者):
它为创建一个产品Product对象的各个部件指定抽象接口,在该接口中一般声明两类方法,一类方法是buildPartX(),它们用于创建复杂对象的各个部件;另一类方法是GetResult(),它们用于返回复杂对象。AbstractBuilder既可以是抽象类,也可以是接口。
2、ConcreteBuilder(具体建造者):
它实现了AbstractBuilder接口,实现各个部件的具体构造和装配方法,定义并明确它所创建的复杂对象,也可以提供一个方法返回创建好的复杂产品对象。
3、Product(产品角色):
它是被构建的复杂对象,包含多个组成部件,具体建造者创建该产品的内部表示并定义它的装配过程。
4、Director(导演):
负责安排复杂对象的建造次序,导演与抽象建造者之间存在关联关系,可以在其Construct()建造方法中调用建造者对象的部件构造与装配方法,完成复杂对象的建造。客户端一般只需要与指挥者进行交互,在客户端确定具体建造者的类型,并实例化具体建造者对象(也可以通过配置文件和反射机制),然后通过指挥者类的构造函数将该对象传入指挥者类中。
三、建造者模式典型代码结构
public class Product
{
public string PartA { get; set; }
public string PartB { get; set; }
public string PartC { get; set; }
} public abstract class AbstractBuilder
{
protected Product product = new Product();
public abstract void BuildPartA();
public abstract void BuildPartB();
public abstract void BuildPartC();
public Product GetResult()
{
return product;
}
} public class ConreteBuilder : AbstractBuilder
{
public override void BuildPartA()
{
this.product.PartA = "PartA";
} public override void BuildPartB()
{
this.product.PartB = "PartB";
} public override void BuildPartC()
{
this.product.PartC = "PartC";
}
} public class Director
{
private AbstractBuilder builder;
public Director(AbstractBuilder builder)
{
this.builder = builder;
} public Product Construct()
{
builder.BuildPartA();
builder.BuildPartB();
builder.BuildPartC();
return builder.GetResult();
}
}
客户端调用:
static void Main(string[] args)
{
AbstractBuilder builder = new ConreteBuilder();
Director director = new Director(builder);
Product product = director.Construct(); Console.WriteLine(product.PartA);
Console.WriteLine(product.PartB);
Console.WriteLine(product.PartC); Console.ReadKey();
}
输出结果:
四、建造者模式实例
我们用本文开头提出的手组成的例子来挑选几个核心部件来构造几个型号的手机,使用建造者模式。
1、X1型手机:CUP 骁龙 835, RAM 6GB ,屏幕 刘海全屏, 硬盘 64GB。
2、X2 型手机: CUP 麒麟 930, RAM 8G, 屏幕 全面屏, 硬盘 128GB。
3、X3型手机: CPU 骁龙 960 RAM 10G,屏幕 超清全面屏 256GB。
UML 图如下:
代码:
public class Mobile
{
public string CPU { get; set; }
public string Screen { get; set; }
public string RAM { get; set; }
public string Disk { get; set; }
}
public abstract class MobileBuilder
{
protected Mobile mobile = new Mobile();
public abstract void BuildCPU();
public abstract void BuildScreen();
public abstract void BuildRAM();
public abstract void BuildDisk();
public Mobile GetMobile()
{
return this.mobile;
}
} public class X1Builder:MobileBuilder
{
public override void BuildCPU()
{
mobile.CPU = "[X1]:CPU had been built.";
} public override void BuildScreen()
{
mobile.Screen = "[X1]:Screen had been built.";
} public override void BuildRAM()
{
mobile.RAM = "[X1]:RAM had been built.";
} public override void BuildDisk()
{
mobile.Disk = "[X1]:Disk had been built.";
}
} public class X2Builder:MobileBuilder
{
public override void BuildCPU()
{
mobile.CPU = "[X2]:CPU had been built.";
} public override void BuildScreen()
{
mobile.Screen = "[X2]:Screen had been built.";
} public override void BuildRAM()
{
mobile.RAM = "[X2]:RAM had been built.";
} public override void BuildDisk()
{
mobile.Disk = "[X2]:Disk had been built.";
}
} public class X3Builder:MobileBuilder
{
public override void BuildCPU()
{
mobile.CPU = "[X3]:CPU had been built.";
} public override void BuildScreen()
{
mobile.Screen = "[X3]:Screen had been built.";
} public override void BuildRAM()
{
mobile.RAM = "[X3]:RAM had been built.";
} public override void BuildDisk()
{
mobile.Disk = "[X3]:Disk had been built.";
}
} public class MobileDirector
{
private MobileBuilder builder;
public MobileDirector(MobileBuilder builder)
{
this.builder=builder;
} public Mobile GetMobile(){
this.builder.BuildCPU();
this.builder.BuildRAM();
this.builder.BuildScreen();
this.builder.BuildDisk();
return this.builder.GetMobile();
}
}
调用代码如下:
static void Main(string[] args)
{
MobileBuilder builder=new X1Builder();
MobileDirector mobileDrector = new MobileDirector(builder);
Mobile mobile = mobileDrector.GetMobile();
Console.WriteLine(mobile.CPU);
Console.WriteLine(mobile.Screen);
Console.WriteLine(mobile.RAM);
Console.WriteLine(mobile.Disk);
Console.ReadKey();
}
输出结果:
如果想生产X2型号的手机,只需要将具体建造者代码修改一下就可以了,即将下面的一行代码:
MobileBuilder builder=new X1Builder();
改成:
MobileBuilder builder=new X2Builder();
输出结果:
也可以将具体建造者类配置在配置文件中,通过反射来创建建造者对象进而创建出新的型号的手机。
在配置文件中加入如下配置:
<appSettings>
<add key="Builder" value="DesignPattern.Builder.MobileInstance.X3Builder"/>
</appSettings>
客户端调用代码如下:
static void Main(string[] args)
{
MobileBuilder builder;
var setting = ConfigurationSettings.AppSettings["Builder"];
var obj = Type.GetType(setting);
if (obj == null) return;
builder = Activator.CreateInstance(obj) as MobileBuilder; if (builder == null) return; MobileDirector mobileDrector = new MobileDirector(builder);
Mobile mobile = mobileDrector.GetMobile();
Console.WriteLine(mobile.CPU);
Console.WriteLine(mobile.Screen);
Console.WriteLine(mobile.RAM);
Console.WriteLine(mobile.Disk);
Console.ReadKey();
}
输出结果:
五、建造者模式的优点
- 在建造者模式中,客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
- 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。由于指挥者类针对抽象建造者编程,增加新的具体建造者无须修改原有类库的代码,系统扩展方便,符合“开闭原则(OCP)“
- 可以更加精细地控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程
六、建造者模式的缺点
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,例如很多组成部分都不相同,不适合使用建造者模式,因此其使用范围受到一定的限制。
- 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,增加系统的理解难度和运行成本。
七、建造者模式的使用场景
- 需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性。
- 需要生成的产品对象的属性相互依赖,需要指定其生成顺序。
- 对象的创建过程独立于创建该对象的类。在建造者模式中通过引入了指挥者类,将创建过程封装在指挥者类中,而不在建造者类和客户类中。
- 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。
八、扩展
Director在建造者模式中扮演着重要的角色,Director类看似简单但是作用却非常大,它决定复杂对象各个部分的创建顺序并且将构建对象的过程和具体建造者隔离,比如创建一个房子,首先肯定要做的事情是打地基,然后是磊墙,然后是封顶,最后是装修,这个过程是有顺序的且必须是这个顺序,其它过程不无法完成房子的建造。
Director'就好像是拍电影导演一样,导演要拍一部电影,需要拍摄若干影像片段,最后由剪辑师做剪接拼接,导演最后会进行最终的剪辑排版合成一个长片-- 电影。那么电影就充当了建造者模式中的复杂产品,拍摄的影像片段就是产片的各个部分,剪辑就是建造者模式的具体具体建造者,剧本是建造者模式的抽象建造者,导演就是Director。那么在在这个过程中,编剧是不可以兼任导演? 剪辑师是不是也可以兼任导演呢?答案是肯定的。
1.抽象建造者干了Director的活
那上面手机建造的实例,删除掉Diretor, 将创建复杂对象的逻辑放到抽象建造者中,并且使子类方法不能重写父类中的建造产品的方法,引用之前的配置代码演变成如下:
public class Mobile
{
public string CPU { get; set; }
public string Screen { get; set; }
public string RAM { get; set; }
public string Disk { get; set; }
}
public abstract class MobileBuilder
{
protected Mobile mobile = new Mobile();
public abstract void BuildCPU();
public abstract void BuildScreen();
public abstract void BuildRAM();
public abstract void BuildDisk();
public static Mobile GetMobile(MobileBuilder builder)
{
builder.BuildCPU();
builder.BuildScreen();
builder.BuildRAM();
builder.BuildDisk();
return builder.mobile;
}
} public class X1Builder : MobileBuilder
{
public override void BuildCPU()
{
mobile.CPU = "[X1]:CPU had been built.";
} public override void BuildScreen()
{
mobile.Screen = "[X1]:Screen had been built.";
} public override void BuildRAM()
{
mobile.RAM = "[X1]:RAM had been built.";
} public override void BuildDisk()
{
mobile.Disk = "[X1]:Disk had been built.";
}
} public class X2Builder : MobileBuilder
{
public override void BuildCPU()
{
mobile.CPU = "[X2]:CPU had been built.";
} public override void BuildScreen()
{
mobile.Screen = "[X2]:Screen had been built.";
} public override void BuildRAM()
{
mobile.RAM = "[X2]:RAM had been built.";
} public override void BuildDisk()
{
mobile.Disk = "[X2]:Disk had been built.";
}
} public class X3Builder : MobileBuilder
{
public override void BuildCPU()
{
mobile.CPU = "[X3]:CPU had been built.";
} public override void BuildScreen()
{
mobile.Screen = "[X3]:Screen had been built.";
} public override void BuildRAM()
{
mobile.RAM = "[X3]:RAM had been built.";
} public override void BuildDisk()
{
mobile.Disk = "[X3]:Disk had been built.";
}
}
调用代码:
static void Main(string[] args)
{
MobileBuilder builder;
var setting = ConfigurationSettings.AppSettings["Builder"];
var obj = Type.GetType(setting);
if (obj == null) return;
builder = Activator.CreateInstance(obj) as MobileBuilder; if (builder == null) return; Mobile mobile = builder.GetMobile();
Console.WriteLine(mobile.CPU);
Console.WriteLine(mobile.Screen);
Console.WriteLine(mobile.RAM);
Console.WriteLine(mobile.Disk);
Console.ReadKey();
}
输出结果:
这种在抽象建造者中使用了一个静态方法来创建产品的做法的好处是产品创建出来的一致性很好,创建产品流程被统一封装,一般不会有差异,这种方式抽象类控制了产品建造的顺序,并且所有的产品的创建顺序都不能改变了(如造房子的流程),对于要求创建顺序一致,并且产品部件的创建都路程一致的产品来说这是一个优点。
但是如果想创建出来的产品有差异,每一个产品的顺序都不一样那该怎么办呢?比如现在这种方法创建出来的顺序是: CPU=》Screen=》RAM=》Disk, 那么我要想X3型号的手机创建顺序变成:CPU=》RAM=》Disk=》Screen。 这种方法就显得不灵活了,没有办法做到个性化了。处理典型的将建造过程的控制权交给Director外,还可以不用Director来完成吗?
2、具体建造者和抽象建造者都可以干Director的活
这里我们依然删掉Director, 将抽象方法中的创建方法变成子类可以重写的虚方法就可以了,然后在具体建造者中重写抽象建造者的创建方法就可以了。
现在我们创建X1,X2,X3型号的手机的顺序分别是这样的:
X1: CPU=》Screen=》RAM=》Disk
X2:CPU=》RAM=》Disk=》Screen
X3:CPU=》Disk=》RAM=》Screen
代码如下:
public class Mobile
{
public string CPU { get; set; }
public string Screen { get; set; }
public string RAM { get; set; }
public string Disk { get; set; }
}
public abstract class MobileBuilder
{
protected Mobile mobile = new Mobile();
public abstract void BuildCPU();
public abstract void BuildScreen();
public abstract void BuildRAM();
public abstract void BuildDisk();
public virtual Mobile GetMobile()
{
this.BuildCPU();
this.BuildScreen();
this.BuildRAM();
this.BuildDisk();
Console.WriteLine(mobile.CPU);
Console.WriteLine(mobile.Screen);
Console.WriteLine(mobile.RAM);
Console.WriteLine(mobile.Disk); return this.mobile;
}
} public class X1Builder : MobileBuilder
{
public override void BuildCPU()
{
mobile.CPU = "[X1]:CPU had been built.";
} public override void BuildScreen()
{
mobile.Screen = "[X1]:Screen had been built.";
} public override void BuildRAM()
{
mobile.RAM = "[X1]:RAM had been built.";
} public override void BuildDisk()
{
mobile.Disk = "[X1]:Disk had been built.";
}
} public class X2Builder : MobileBuilder
{
public override void BuildCPU()
{
mobile.CPU = "[X2]:CPU had been built.";
} public override void BuildScreen()
{
mobile.Screen = "[X2]:Screen had been built.";
} public override void BuildRAM()
{
mobile.RAM = "[X2]:RAM had been built.";
} public override void BuildDisk()
{
mobile.Disk = "[X2]:Disk had been built.";
} public override Mobile GetMobile()
{
BuildCPU();
BuildRAM();
BuildDisk();
BuildScreen();
Console.WriteLine(mobile.CPU);
Console.WriteLine(mobile.RAM);
Console.WriteLine(mobile.Disk);
Console.WriteLine(mobile.Screen);
return this.mobile;
}
} public class X3Builder : MobileBuilder
{
public override void BuildCPU()
{
mobile.CPU = "[X3]:CPU had been built.";
} public override void BuildScreen()
{
mobile.Screen = "[X3]:Screen had been built.";
} public override void BuildRAM()
{
mobile.RAM = "[X3]:RAM had been built.";
} public override void BuildDisk()
{
mobile.Disk = "[X3]:Disk had been built.";
}
public override Mobile GetMobile()
{
BuildCPU();
BuildDisk();
BuildRAM();
BuildScreen();
Console.WriteLine(mobile.CPU);
Console.WriteLine(mobile.Disk);
Console.WriteLine(mobile.RAM);
Console.WriteLine(mobile.Screen);
return this.mobile;
}
}
调用代码:
static void Main(string[] args)
{
MobileBuilder builder;
var setting = ConfigurationSettings.AppSettings["Builder"];
var obj = Type.GetType(setting);
if (obj == null) return;
builder = Activator.CreateInstance(obj) as MobileBuilder; if (builder == null) return; Mobile mobile = builder.GetMobile(); Console.ReadKey();
}
输出结果:
3. 控制某些部件不用被创建
假如要造一个X4型号的手机,这个手机支持NFC,我们知道X1,X2,X3中都不支持NFC,那怎么办呢?,我们可以给抽象建造者类加一个方法,叫 HasNFC()并且返回bool值,并将其设置成默认值为false。 修改抽象建造者的GetMobile() 方法,只有当HasNFC()返回 true是才创建NFC模块,并且在X4Builder的具体建造者类中重写HasNFC()方法使其返回true就可以了。
代码如下:
public class Mobile
{
public string CPU { get; set; }
public string Screen { get; set; }
public string RAM { get; set; }
public string Disk { get; set; }
}
public abstract class MobileBuilder
{
protected Mobile mobile = new Mobile();
public abstract void BuildCPU();
public abstract void BuildScreen();
public abstract void BuildRAM();
public abstract void BuildDisk();
public virtual void BuildNFC()
{
Console.WriteLine("NFC had been built.");
}
protected virtual bool HasNFC()
{
return false;
}
public virtual Mobile GetMobile()
{
this.BuildCPU();
this.BuildScreen();
this.BuildRAM();
this.BuildDisk();
if(HasNFC())
{
this.BuildNFC();
}
Console.WriteLine(mobile.CPU);
Console.WriteLine(mobile.Screen);
Console.WriteLine(mobile.RAM);
Console.WriteLine(mobile.Disk); return this.mobile;
}
} public class X1Builder : MobileBuilder
{
public override void BuildCPU()
{
mobile.CPU = "[X1]:CPU had been built.";
} public override void BuildScreen()
{
mobile.Screen = "[X1]:Screen had been built.";
} public override void BuildRAM()
{
mobile.RAM = "[X1]:RAM had been built.";
} public override void BuildDisk()
{
mobile.Disk = "[X1]:Disk had been built.";
}
} public class X2Builder : MobileBuilder
{
public override void BuildCPU()
{
mobile.CPU = "[X2]:CPU had been built.";
} public override void BuildScreen()
{
mobile.Screen = "[X2]:Screen had been built.";
} public override void BuildRAM()
{
mobile.RAM = "[X2]:RAM had been built.";
} public override void BuildDisk()
{
mobile.Disk = "[X2]:Disk had been built.";
} public override Mobile GetMobile()
{
BuildCPU();
BuildRAM();
BuildDisk();
BuildScreen();
Console.WriteLine(mobile.CPU);
Console.WriteLine(mobile.RAM);
Console.WriteLine(mobile.Disk);
Console.WriteLine(mobile.Screen);
return this.mobile;
}
} public class X3Builder : MobileBuilder
{
public override void BuildCPU()
{
mobile.CPU = "[X3]:CPU had been built.";
} public override void BuildScreen()
{
mobile.Screen = "[X3]:Screen had been built.";
} public override void BuildRAM()
{
mobile.RAM = "[X3]:RAM had been built.";
} public override void BuildDisk()
{
mobile.Disk = "[X3]:Disk had been built.";
}
public override Mobile GetMobile()
{
BuildCPU();
BuildDisk();
BuildRAM();
BuildScreen();
Console.WriteLine(mobile.CPU);
Console.WriteLine(mobile.Disk);
Console.WriteLine(mobile.RAM);
Console.WriteLine(mobile.Screen);
return this.mobile;
}
}
public class X4Builder : MobileBuilder
{
public override void BuildCPU()
{
mobile.CPU = "[X4]:CPU had been built.";
} public override void BuildScreen()
{
mobile.Screen = "[X4]:Screen had been built.";
} public override void BuildRAM()
{
mobile.RAM = "[X4]:RAM had been built.";
} public override void BuildDisk()
{
mobile.Disk = "[X4]:Disk had been built.";
} protected override bool HasNFC()
{
return true;
}
}
App.Config 配置:
<appSettings>
<add key="Builder" value="DesignPattern.Builder.MobileInstance.X3Builder"/>
</appSettings>
客户端代码:
static void Main(string[] args)
{
MobileBuilder builder; var setting = ConfigurationSettings.AppSettings["Builder"];
var obj = Type.GetType(setting);
if (obj == null) return;
builder = Activator.CreateInstance(obj) as MobileBuilder; if (builder == null) return; Mobile mobile = builder.GetMobile(); Console.ReadKey();
}
输出结果:
修改配置文件,使其造一台X4 如下,调用代码不变:
<appSettings>
<add key="Builder" value="DesignPattern.Builder.MobileInstance.X4Builder"/>
</appSettings>
输出结果:
好了建造者模式就探讨到这里。
【设计模式】建造者模式 Builder Pattern的更多相关文章
- 设计模式 - 建造者模式 Builder Pattern
简介 场景 在创建复杂对象时,用户无须关心该对象所包含的属性以及它们的组装方式,只需要指定复杂对象的类型和内容就可以构建它们. 模式定义 建造者模式:将一个复杂对象的构建与表示分离,使得同样的构建过程 ...
- 23种设计模式--建造者模式-Builder Pattern
一.建造模式的介绍 建造者模式就是将零件组装成一个整体,用官方一点的话来讲就是将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示.生活中比如说组装电脑,汽车等等这些都是建 ...
- 【原】iOS设计模式之:建造者模式Builder Pattern,用于改进初始化参数
本文主要讨论一下iOS中的Builder Pattern.与网上很多版本不同,本文不去长篇大论地解释建造者模式的概念,那些东西太虚了.设计模式这种东西是为了解决实际问题的,不能为了设计模式而设计模式, ...
- 乐在其中设计模式(C#) - 建造者模式(Builder Pattern)
原文:乐在其中设计模式(C#) - 建造者模式(Builder Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 建造者模式(Builder Pattern) 作者:webabc ...
- iOS设计模式之:建造者模式Builder Pattern,用于改进初始化参数
转自:http://www.cnblogs.com/wengzilin/p/4365855.html 本文主要讨论一下iOS中的Builder Pattern.与网上很多版本不同,本文不去长篇大论地解 ...
- 设计模式系列之建造者模式(Builder Pattern)——复杂对象的组装与创建
说明:设计模式系列文章是读刘伟所著<设计模式的艺术之道(软件开发人员内功修炼之道)>一书的阅读笔记.个人感觉这本书讲的不错,有兴趣推荐读一读.详细内容也可以看看此书作者的博客https:/ ...
- 二十四种设计模式:建造者模式(Builder Pattern)
建造者模式(Builder Pattern) 介绍将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. 示例用同样的构建过程创建Sql和Xml的Insert()方法和Get()方 ...
- 建造者模式(Builder Pattern)
建造者模式(Builder Pattern) 它可以将多个简单的对象一步一步构建成一个复杂的对象. 意图:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示. 主要解决:主要解决在软 ...
- 设计模式—建造者模式(Builder)
title: 设计模式-建造者模式 建造者模式(Builder)是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节.建造者模式属于对 ...
随机推荐
- 分享波面经【2年经验】【linux c++】
快三个月没写博客了,一直在忙着准备面试和去面试的路上,所以没时间写,也没什么想写的.现在告一段落,就总结一波! 面经 很感谢一些公司能给我面试机会,有的公司真的会拿学历卡人,也不想多说! 17年毕业, ...
- layui选项卡同步问题
下面这些代码是在有选项卡的情况下, 一个页面的状态修改时打开另一个选项卡, 另一个选项卡修改成功后,可以使你当前的选项卡状态实时更新 // 重载当前的页面的需要刷新的表格 table.reload(' ...
- 安卓开发笔记(二十):利用夜神模拟器调试运行Android Studio的apk
一.首先来到夜神模拟器的安装目录下 如下图所示: 再把这整个文件夹添加到我们的windows环境变量里.然后再把android studio 和夜神模拟器都打开,注意必须同时打开而且不能够把夜神模拟器 ...
- 安装windows 10到固态硬盘实践记录
1.前提 由于之前一直用的机械硬盘,电脑用了几年是越来越慢,所以打算买个SSD,装个新系统,其他的机械硬盘都当从盘用 2.准备工作 SSD :256G 3星的 WIN10正版光盘一张 外置光驱一个 3 ...
- winfrom 改变图片透明度 Alpha
效果图: 核心代码: /// <summary> /// 方法一 设置图像透明度 /// </summary> /// <param name="srcImag ...
- C#运算符的简单使用测试
在代码中看到的代码中|=,有点不太理解故重新学习了下位运算符. 位运算符在 c# 中的测试用例 [TestMethod] public void TestMethod1() { var a = fal ...
- 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之二 || 后端项目搭建
前言 至于为什么要搭建.Net Core 平台,这个网上的解释以及铺天盖地,想了想,还是感觉重要的一点,跨平台,嗯!没错,而且比.Net 更容易搭建,速度也更快,所有的包均有Nuget提供,不再像以前 ...
- Python基础(迭代器)
一.迭代器 概述: 迭代是Python最强大的功能之一,是访问集合元素的一种方式. 迭代器是一个可以记住遍历的位置的对象. 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能 ...
- 6.Flask-WTForms
Flask-WTF是简化了WTForms操作的一个第三方库.WTForms表单的两个主要功能是验证用户提交数据的合法性以及渲染模板.还有其它一些功能:CSRF保护, 文件上传等.安装方法:pip in ...
- ASP.NET Core中使用GraphQL - 第九章 在GraphQL中处理多对多关系
ASP.NET Core中使用GraphQL ASP.NET Core中使用GraphQL - 第一章 Hello World ASP.NET Core中使用GraphQL - 第二章 中间件 ASP ...