开篇

天天逛博客园,就是狠不下心来写篇博客,忙是一方面,但是说忙能有多忙呢,都有时间逛博客园,写篇博客的时间都没有?(这还真不好说)

每次想到写一篇新的设计模式,我总会问自己:

1,自己理解了吗?

2,我能以一种简单且有逻辑的方式把它说出来吗?

不说做到有的放矢,但是一本正经的胡说八道还是要有吧,起码要忽悠得头头是道嘛(手动斜眼笑)

关于工厂模式的几个问题

1,这个是拿来干什么的?

2,怎么用?

3,不用行不行?

第一个和第三个问题,我现在就可以告诉你答案:早点下班,可以

所有的设计模式对我来说都是为了减少工作量。关于减少工作量我的理解是:每个需求,都应该在它适当的时候出现适当的代码!这个太重要了

代码偷懒,后期返工多

过度设计,后期返工多

设计模式+经验可以解决这个问题,其他的我还不知道。没有经验怎么办?两个要点:

1,能用

2,简洁

首先要达到能用,然后就是尽量简洁,这样代码就不会太差。首先你要自己看得懂,然后是让队友看得懂。

你知道你队友看到一堆烂的看都看不懂,也一句注释都没有的代码的时候的心理阴影面积吗?

这其实也没什么,谁没填过别人的坑呢?关键是他知道你家在哪里,而且还知道你经常走夜路,就问你怕不怕?(卧槽,又跑题了。。)

需求:你有一个披萨店,只卖一种披萨,代码如下:

披萨:

import java.util.ArrayList;
import java.util.List; /**
* 披萨类
* @author skysea
*/
public class Pizza { private String name;//披萨名称 private String dough;//面团 private String sauce;//酱料 private List<String> toppings = new ArrayList<>();//佐料 public Pizza() {
this.name = "原味披萨";
this.dough = "原味面团";
this.sauce = "原味酱料";
} void prepare() {
System.out.println("开始准备披萨:" + name); System.out.println("开始处理面团:" + dough); System.out.println("添加酱料:" + sauce); System.out.println("添加佐料:");
if(toppings.size() > 0) {
for(String t : toppings) {
System.out.println(" " + t);
}
}
} void bake() {
System.out.println("烘焙25分钟..");
} void cut() {
System.out.println("披萨切片..");
} void box() {
System.out.println("披萨打包..");
} public String getName() {
return name;
}
}

披萨店:

/**
* 只卖一种披萨的披萨店
* @author skysea
*/
public class PizzaStore { public Pizza orderPizza() {
Pizza pizza = new Pizza();
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}

测试类:

/**
* pizza测试类
* @author skysea
*/
public class PizzaTest {
public static void main(String[] args) {
PizzaStore pizzaStore = new PizzaStore();
Pizza pizza = pizzaStore.orderPizza();
System.out.println("当前预定的披萨:" + pizza.getName());
}
}

现在披萨店要拓展业务了,因为卖一种披萨顾客已经吃腻了,现在要开始添加新的披萨类型

简单工厂模式

Pizza类的改进

import java.util.ArrayList;
import java.util.List; /**
* 披萨抽象类
* 1,修改private -> protected(保证子类拥有这些属性)
* 2,将Pizza定义为abstract类,防止被new,也是为后面的改造做准备
* @author skysea
*/
public abstract class Pizza { protected String name;//披萨名称 protected String dough;//面团 protected String sauce;//酱料 protected List<String> toppings = new ArrayList<>();//佐料 void prepare() {
System.out.println("开始准备披萨:" + name); System.out.print("开始处理面团:" + dough); System.out.println("添加酱料:" + sauce); System.out.println("添加佐料:");
for(String t : toppings) {
System.out.println(" " + t);
}
} void bake() {
System.out.println("烘焙25分钟..");
} void cut() {
System.out.println("披萨切片..");
} void box() {
System.out.println("披萨打包..");
} public String getName() {
return name;
} @Override
public String toString() {
return "Pizza [name=" + name + ", dough=" + dough + ", sauce=" + sauce + ", toppings=" + toppings + "]";
}
}

先给出新增的披萨

芝士披萨:

/**
* 芝士披萨
* @author skysea
*/
public class CheesePizza extends Pizza{
public CheesePizza() {
this.name = "芝士披萨";
this.dough = "芝士披萨的面团";
this.sauce = "芝士披萨的酱料";
this.toppings.add("很多芝士....");
}
}

蛤蜊披萨:

/**
* 蛤蜊披萨
* @author skysea
*/
public class ClamPizza extends Pizza {
public ClamPizza() {
this.name = "蛤蜊披萨";
this.dough = "蛤蜊披萨的面团";
this.sauce = "蛤蜊披萨的酱料";
this.toppings.add("蛤蜊");
}
}

意大利烤肠披萨:

/**
* 意大利烤肠披萨
* @author skysea
*/
public class PepperoniPizza extends Pizza{ public PepperoniPizza() {
this.name = "意大利烤肠披萨";
this.dough = "意大利烤肠披萨的面团";
this.sauce = "意大利烤肠披萨的酱料";
this.toppings.add("一大波意大利烤肠...");
}
}

素食比萨:

/**
* 素食比萨
* @author skysea
*/
public class VeggiePizza extends Pizza {
public VeggiePizza() {
name = "素食比萨";
dough = "素食比萨的面团";
sauce = "素食比萨的酱料";
toppings.add("素食比萨");
toppings.add("素食比萨佐料1");
toppings.add("素食比萨佐料2");
}
}

贴了这么多代码,先给出一波简单的实现:

/**
* pizza店
* @author skysea
*/
public class PizzaStore { public Pizza orderPizza(String type) {
Pizza pizza = null;
if (type.equals("cheese")) {
pizza = new CheesePizza();
} else if (type.equals("pepperoni")) {
pizza = new PepperoniPizza();
} else if (type.equals("clam")) {
pizza = new ClamPizza();
} else if (type.equals("veggie")) {
pizza = new VeggiePizza();
} pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box(); return pizza;
} }

在不考虑继续扩展披萨种类的时候,这样的实现有没有问题,一般来说,达到了可以用的标准,但是不好用,问题如下:

1,没有兼容原来的一种披萨方法 public Pizza orderPizza(),相信我,每一个public方法都是很重要的,因为你不知道有多少地方用到过。当然也不是没办法知道,只是你知道也不一定就能改,就算你能改,也不一定改对。

2,String类型的type太容易出错了,个人感觉对程序开发不友好,当然这个也要分情况,灵活和严谨本来就很难做到两全

3,推荐取不到合适的type时抛异常,而不是返回空,便于排查问题(此处的if里面只是直接new返回的对象,实际情况远比现在的复杂)

给出第二版:

/**
* pizza店
* @author skysea
*/
public class PizzaStore { public Pizza orderPizza() {
return orderPizza(PizzaTypeEnum.CHEESE);
} public Pizza orderPizza(PizzaTypeEnum type) {
Pizza pizza; pizza = SimplePizzaFactory.getPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box(); return pizza;
}
}

SimplePizzaFactory:

/**
* 简单工厂类
* @author skysea
*/
public class SimplePizzaFactory { /**
* 根据类型获取pizza
* @param type
* @return
*/
public static final Pizza getPizza(PizzaTypeEnum type){
switch (type) {
case CHEESE: return new CheesePizza();
case CLAM: return new ClamPizza();
case PEPPERONI: return new PepperoniPizza();
case VEGGIE: return new VeggiePizza();
default: throw new NoSuchPizzaException(type.getCode());
}
}
}

辅助类(枚举,异常):

/**
* 定义pizza类型枚举
* @author skysea
*
*/
public enum PizzaTypeEnum{
/**
* 芝士披萨
*/
CHEESE("cheese"),
/**
* 意大利烤肠披萨
*/
PEPPERONI("pepperoni"),
/**
* 蛤蜊披萨
*/
CLAM("clam"),
/**
* 素食比萨
*/
VEGGIE("veggie");
private final String code;
PizzaTypeEnum(String code) {
this.code = code;
}
public String getCode() {
return code;
}
}
/**
* 没有匹配的pizza异常
* @author skysea
*/
public class NoSuchPizzaException extends RuntimeException{
private static final long serialVersionUID = 6831396172306375611L;
public NoSuchPizzaException(String message) {
super(message);
}
}

测试类:

/**
* pizza测试类
* @author skysea
*/
public class PizzaTest { public static void main(String[] args) {
PizzaStore store = new PizzaStore(); Pizza pizza = store.orderPizza(PizzaTypeEnum.CHEESE);
System.out.println(pizza); pizza = store.orderPizza(PizzaTypeEnum.VEGGIE);
System.out.println(pizza);
}
}

好了,代码写到这里,其实对于:新增披萨类型的这个需求的实现其实已经很好了。至少来说现阶段的需求实现了,其次就是对调用方友好,至少队友不会跑过来问你类型传啥,不会告诉你他string字符串传错了,不会在你改个类型的时候,还要通知他(当然这个也可以通过常量来处理)。

吹了半天,来说说这段代码的问题,正常情况下,需求会是这样变:

1,PepperoniPizza暂时不要了,一般来说,你问他要不要,他会说,这个要看后面的运营情况(我:...)

2,你给我新加一个xx披萨

现在需要改的是两个地方,一个是工厂类,一个是枚举,但是主要的流程是不用改了,如果你觉得还是很麻烦在不考虑性能的情况下,你还可以用反射来玩,改造一下工厂类(实现通过class来创建对象)和枚举(添加一个字段来存放type对应的class)就可以了,不赘述..

第一波需求就差不多可以这样收手了,随着业务的发展,披萨店那叫一个红火啊,虽然中间也对代码做了很多新的披萨,但是由于PizzaStore相当稳定,也没出什么大问题。

新的问题(开分店):

1,旗舰店在芝加哥,现在要在纽约开一家新的店

2,分店的披萨口味要根据当地的口味来进行调整,保证能够不失品牌特色的同时,也能满足当地独特的风味

3,分店披萨的种类与暂时与旗舰店保持一致

工厂方法模式

先把所有的披萨列出来

芝加哥的披萨:

/**
* 芝加哥芝士披萨
* @author skysea
*/
public class ChicagoStyleCheesePizza extends Pizza { public ChicagoStyleCheesePizza() {
name = "芝加哥芝士披萨";
dough = "芝加哥芝士披萨面团";
sauce = "芝加哥芝士披萨酱料"; toppings.add("芝加哥芝士披萨调料1");
toppings.add("芝加哥芝士披萨调料2");
} @Override
void cut() {
System.out.println("芝加哥芝士披萨版切片...");
}
}
/**
* 芝加哥蛤蜊披萨
* @author skysea
*/
public class ChicagoStyleClamPizza extends Pizza {
public ChicagoStyleClamPizza() {
name = "芝加哥蛤蜊披萨";
dough = "芝加哥蛤蜊披萨面团";
sauce = "芝加哥蛤蜊披萨酱料"; toppings.add("芝加哥蛤蜊披萨佐料1");
toppings.add("芝加哥蛤蜊披萨佐料2");
} @Override
void cut() {
System.out.println("芝加哥蛤蜊披萨版切片...");
}
}
/**
* 芝加哥意大利烤肠披萨
* @author skysea
*/
public class ChicagoStylePepperoniPizza extends Pizza {
public ChicagoStylePepperoniPizza() {
name = "芝加哥意大利烤肠披萨";
dough = "芝加哥意大利烤肠披萨面团";
sauce = "芝加哥意大利烤肠披萨酱料"; toppings.add("芝加哥意大利烤肠披萨调料1");
toppings.add("芝加哥意大利烤肠披萨调料2");
toppings.add("芝加哥意大利烤肠披萨调料3");
toppings.add("芝加哥意大利烤肠披萨调料4");
} @Override
void cut() {
System.out.println("芝加哥意大利烤肠披萨版切片...");
}
}
/**
* 芝加哥素食比萨
* @author skysea
*/
public class ChicagoStyleVeggiePizza extends Pizza {
public ChicagoStyleVeggiePizza() {
name = "芝加哥素食比萨";
dough = "芝加哥素食比萨的面团";
sauce = "芝加哥素食比萨的酱料"; toppings.add("芝加哥素食比萨调料1");
toppings.add("芝加哥素食比萨调料2");
toppings.add("芝加哥素食比萨调料3");
} void cut() {
System.out.println("芝加哥素食比萨版切片...");
}
}

纽约的披萨:

/**
* 纽约芝士披萨
* @author skysea
*/
public class NYStyleCheesePizza extends Pizza { public NYStyleCheesePizza() {
name = "纽约芝士披萨";
dough = "纽约芝士披萨面团";
sauce = "纽约芝士披萨酱料"; toppings.add("纽约芝士披萨调料1");
toppings.add("纽约芝士披萨调料2");
} @Override
void cut() {
System.out.println("纽约芝士披萨版切片...");
}
}
/**
* 纽约蛤蜊披萨
* @author skysea
*/
public class NYStyleClamPizza extends Pizza {
public NYStyleClamPizza() {
name = "纽约蛤蜊披萨";
dough = "纽约蛤蜊披萨面团";
sauce = "纽约蛤蜊披萨酱料"; toppings.add("纽约蛤蜊披萨佐料1");
toppings.add("纽约蛤蜊披萨佐料2");
} @Override
void cut() {
System.out.println("纽约蛤蜊披萨版切片...");
}
}
/**
* 纽约意大利烤肠披萨
* @author skysea
*/
public class NYStylePepperoniPizza extends Pizza {
public NYStylePepperoniPizza() {
name = "纽约意大利烤肠披萨";
dough = "纽约意大利烤肠披萨面团";
sauce = "纽约意大利烤肠披萨酱料"; toppings.add("纽约意大利烤肠披萨调料1");
toppings.add("纽约意大利烤肠披萨调料2");
toppings.add("纽约意大利烤肠披萨调料3");
toppings.add("纽约意大利烤肠披萨调料4");
} @Override
void cut() {
System.out.println("纽约意大利烤肠披萨版切片...");
}
}
/**
* 纽约素食比萨
* @author skysea
*/
public class NYStyleVeggiePizza extends Pizza {
public NYStyleVeggiePizza() {
name = "纽约素食比萨";
dough = "纽约素食比萨的面团";
sauce = "纽约素食比萨的酱料"; toppings.add("纽约素食比萨调料1");
toppings.add("纽约素食比萨调料2");
toppings.add("纽约素食比萨调料3");
} void cut() {
System.out.println("纽约素食比萨版切片...");
}
}

披萨倒是列完了,但是在实际的开发过程中,业务逻辑这么简单那是不可能的,想要改那什么旗舰店披萨的类名是很困难的

一般要考虑:

1,是不是单机,有没有其他外部系统在调用

2,改动原来的代码有什么好处,更容易理解吗?迭代了几个版本过后垃圾代码太多了吗?

3,影响大不大

当然,我这里是随便造,你们呢,我就不知道了,嘿嘿嘿,所以碰到这种情况,一般来说要悠着点,看时间,也要看影响,开发就是这样,同一个功能,2天有2天的做法,5天有5天的做法,10天有10天的做法

披萨店改造:

/**
* 披萨店抽象类
* @author skysea
*/
public abstract class PizzaStore { abstract Pizza createPizza(String item); public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type);
System.out.println("--- 制作 " + pizza.getName() + " ---");
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
/**
* 芝加哥披萨店
* @author skysea
*/
public class ChicagoPizzaStore extends PizzaStore {
public static final String CHEESE = "cheese";
public static final String VEGGIE = "veggie";
public static final String CLAM = "clam";
public static final String PEPPERONI = "pepperoni";
Pizza createPizza(String item) {
if (CHEESE.equals(item)) {
return new ChicagoStyleCheesePizza();
} else if (VEGGIE.equals(item)) {
return new ChicagoStyleVeggiePizza();
} else if (CLAM.equals(item)) {
return new ChicagoStyleClamPizza();
} else if (PEPPERONI.equals(item)) {
return new ChicagoStylePepperoniPizza();
} else {
throw new NoSuchPizzaException(item);
}
}
}

纽约披萨店(和芝加哥披萨店几乎一毛一样,这里就不展开了):

/**
* 纽约披萨店
* @author skysea
*/
public class NYPizzaStore extends PizzaStore { public static final String CHEESE = "cheese";
public static final String VEGGIE = "veggie";
public static final String CLAM = "clam";
public static final String PEPPERONI = "pepperoni";
Pizza createPizza(String item) {
if (CHEESE.equals(item)) {
return new NYStyleCheesePizza();
} else if (VEGGIE.equals(item)) {
return new NYStyleVeggiePizza();
} else if (CLAM.equals(item)) {
return new NYStyleClamPizza();
} else if (PEPPERONI.equals(item)) {
return new NYStylePepperoniPizza();
} else {
throw new NoSuchPizzaException(item);
}
}
}

这段代码有三个问题要理解清楚:

1,这个地方为啥要弄个抽象类出来?

这个就要结合实际来理解了,分店与分店之间,需不需要统一规范化管理?需不需要保证自己的特色?答案毫无疑问,都是需要的

这个地方制作披萨的过程,毫无疑问是肯定要一致的。就像外卖一样,下单,炒菜,配送。整套流程都是这样,不能说你出来就开始炒菜了啊,这不科学。不一样的地方就是,你炒的什么菜,好不好吃。配送得快不快,稳不稳,服务好不好。

所以,抽象类的意义就是:规范、特色

2,factory咋个不见了?

因为把它和具体的store合并在一起了,这样又引申出另外一个问题:为啥要合并?因为store现在充当的角色就是facotry,刚才说过的制作过程已经放到父类中实现了,现在只需要在具体的store中去解决披萨的创建问题

3,为啥又不用枚举了,弄个String来创建pizza?

如果还是单机,用枚举当然会比直接扔个string来得稳当。

开了分店,要是每个分店都是一套完整的服务在玩,丢个string,要比枚举来得好。原因有2:传输过程中的序列化和反序列化、更加灵活(客户端不用每次都因为这个原因要去升级对应的包,特别是多个版本在跑得时候,升级了又会导致其他东西不能玩)

测试类:

/**
* 披萨测试类
* @author skysea
*/
public class PizzaTest { public static void main(String[] args) {
PizzaStore nyStore = new NYPizzaStore();
PizzaStore chicagoStore = new ChicagoPizzaStore(); Pizza pizza = nyStore.orderPizza(NYPizzaStore.CHEESE);
System.out.println("Ethan ordered a " + pizza.getName() + "\n"); pizza = chicagoStore.orderPizza(ChicagoPizzaStore.CHEESE);
System.out.println("Joel ordered a " + pizza.getName() + "\n"); pizza = nyStore.orderPizza(NYPizzaStore.CLAM);
System.out.println("Ethan ordered a " + pizza.getName() + "\n"); pizza = chicagoStore.orderPizza(ChicagoPizzaStore.CLAM);
System.out.println("Joel ordered a " + pizza.getName() + "\n"); pizza = nyStore.orderPizza(NYPizzaStore.PEPPERONI);
System.out.println("Ethan ordered a " + pizza.getName() + "\n"); pizza = chicagoStore.orderPizza(ChicagoPizzaStore.PEPPERONI);
System.out.println("Joel ordered a " + pizza.getName() + "\n"); pizza = nyStore.orderPizza(NYPizzaStore.VEGGIE);
System.out.println("Ethan ordered a " + pizza.getName() + "\n"); pizza = chicagoStore.orderPizza(ChicagoPizzaStore.VEGGIE);
System.out.println("Joel ordered a " + pizza.getName() + "\n");
}
}

结果(结果太多了,就不全部截图出来了):


headfirst设计模式(4)—工厂模式的更多相关文章

  1. HeadFirst设计模式之工厂模式

    一. 1."工厂模式"不是种真正的设计模式,而是一种编程术语 2.The Factory Method Pattern defi nes an interface for crea ...

  2. HeadFirst 设计模式 04 工厂模式

    除了 new 操作符之外, 还有更多创造对象的方法. 工厂处理创建对象的细节. 这么做的目的是为了抽象, 例如把创建比萨的代码包装进一个类, 当以后实现改变时, 只需修改这个类即可. 利用静态方法定义 ...

  3. 设计模式——抽象工厂模式及java实现

    设计模式--抽象工厂模式及java实现 设计模式在大型软件工程中很重要,软件工程中采用了优秀的设计模式有利于代码维护,方便日后更改和添加功能. 设计模式有很多,而且也随着时间在不断增多,其中最著名的是 ...

  4. 5. 星际争霸之php设计模式--抽象工厂模式

    题记==============================================================================本php设计模式专辑来源于博客(jymo ...

  5. 3. 星际争霸之php设计模式--简单工厂模式

    题记==============================================================================本php设计模式专辑来源于博客(jymo ...

  6. iOS 设计模式之工厂模式

    iOS 设计模式之工厂模式 分类: 设计模式2014-02-10 18:05 11020人阅读 评论(2) 收藏 举报 ios设计模式 工厂模式我的理解是:他就是为了创建对象的 创建对象的时候,我们一 ...

  7. 设计模式之工厂模式(Factory)

    设计模式的工厂模式一共有三种:简单工厂模式,工厂模式,抽象工厂模式 简单工厂模式原理:只有一个工厂类,通过传参的形式确定所创建的产品对象种类 代码如下: #include <stdio.h> ...

  8. php设计模式:工厂模式

    php设计模式:工厂模式 意图: 定义一个用于创建对象的接口,让子类决定实例化哪一个类. 工厂模式实现: 工厂模式中任何创建对象的工厂类都要实现这个接口,实现接口的方法体中都要实现接口中的方法,它声明 ...

  9. 浅析JAVA设计模式之工厂模式(一)

    1 工厂模式简单介绍 工厂模式的定义:简单地说,用来实例化对象,取代new操作. 工厂模式专门负责将大量有共同接口的类实例化.工作模式能够动态决定将哪一个类实例化.不用先知道每次要实例化哪一个类. 工 ...

  10. java 设计模式之工厂模式与反射的结合

    工厂模式: /**  * @author Rollen-Holt 设计模式之 工厂模式  */   interface fruit{     public abstract void eat(); } ...

随机推荐

  1. 安卓自定义控件(二)BitmapShader、ShapeDrawable、Shape

    第一篇博客中,我已经Canvas.Paint.Shader.Xfermode这些对象做了总结,而现在这篇文章主要介绍BitmapShader位图渲染,Xfermode如何实际应用,还有形状的绘制.不过 ...

  2. 逆波兰表达式POJ——2694

    问题描述: 逆波兰表达式是一种吧运算符前置的算术表达式,例如普通的表达式2+3的逆波兰表示为+23.逆波兰表达式的优点是运算符之间不必有优先级的关系,也不必有括号改变运算次序,例如(2+3)*4的逆波 ...

  3. Asp.net Core中SignalR Core预览版的一些新特性前瞻,附源码(消息订阅与发送二进制数据)

    目录 SignalR系列目录(注意,是ASP.NET的目录.不是Core的) 前言 一晃一个月又过去了,上个月有个比较大的项目要验收上线.所以忙的脚不沾地.现在终于可以忙里偷闲,写一篇关于Signal ...

  4. 51nod 1020 逆序排列 DP

    在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序.一个排列中逆序的总数就称为这个排列的逆序数. 如2 4 3 1中,2 1,4 3,4 1,3 1是逆序 ...

  5. POJ-1273-Drainage Ditches 朴素增广路

    Drainage Ditches Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 70588   Accepted: 2743 ...

  6. UWP 图片缩放

    给Image外面包裹一个ScrollViewer,你会回来感激我的. 哦,对了,PC上需要按住Ctrl键,滑动鼠标滑轮即可:手机上双指就可以缩放. <ScrollViewer ZoomMode= ...

  7. javascript设计模式——模板方法模式

    前面的话 在javascript开发中用到继承的场景其实并不是很多,很多时候喜欢用mix-in的方式给对象扩展属性.但这不代表继承在javascript里没有用武之地,虽然没有真正的类和继承机制,但可 ...

  8. Windows中根据端口定位持有者程序

    之前被一个问题所困扰, 电脑过一段时间就无法上网, 查询netstat, 会有大量的端口被占用, 但是看不出来是哪个程序开启的 ( 最终定位到是ADSafe搞得鬼 ) . 后来发现几个命令可以根据端口 ...

  9. Android Weekly Notes Issue #287

    Android Weekly Issue #287 December 10th, 2017 Android Weekly Issue #287 圣诞节快要来了,小编也偷懒了,本期内容包括如何通过AS添 ...

  10. 自动类型安全的.NET标准REST库refit

    在SCOTT HANSELMAN 博客上看到一个好东西<Exploring refit, an automatic type-safe REST library for .NET Standar ...