目的
客户端指定自己需要的数据, 从服务端只向客户端传输必要的数据
例子代码
马上要到年底了, 又要紧急相亲了, 想起我这相亲史不禁潸然泪下, 输给叙利亚的从未赢过的国足版差强人
遥想那是我第一次相亲, 我问 "你多大", 她说 C, 第二次相亲的问我: "你多大", 我说 20 CM, 第三次 ..., 以上都是玩笑了, 其实相亲也没那么难, 只要你穿着整洁, 举止礼貌, 走的时候记得开劳斯莱斯送人家回去就行了
一个男生有很多属性, 常见的身高, 体重, 肺活量, 学历, 性格, 是否有车, 有房, 有钱等, 比如我们这个男生是一个大类, 里面的属性都是存在一个表中或者都是从一个底层接口取的, 这时候我们定义一个接口返回男生的信息:
@Datapublic class Boy { private String name; private Integer height; private Integer weight; private CharacterTypeEnum characterType; private Boolean hasCar; private Boolean hasHouse; private Boolean rich;}
性格枚举内容如下:
@Getterpublic enum CharacterTypeEnum { MATURE_STEADY("成熟稳重"), HUMOROUS("风趣幽默"); CharacterTypeEnum(String desc) { this.desc = desc; } private String desc;}
我们用代码模拟一下这个能力:
public class RestClient { public static final Boolean[] BOOLEAN_ARRAY = {Boolean.TRUE, Boolean.FALSE}; public static final String[] NAME_ARRAY = {"贾宝玉", "武松", "张角"}; public static final Integer[] HEIGHT_ARRAY = {170, 175, 180}; public static final Integer[] WEIGHT_ARRAY = {50, 60, 70}; public static final Random RANDOM = new java.util.Random(); public static Boy queryBoyDetail() { Boy boy = new Boy(); boy.setName(queryRandomItem (NAME_ARRAY)); boy.setHeight(queryRandomItem (HEIGHT_ARRAY)); boy.setWeight(queryRandomItem (WEIGHT_ARRAY)); boy.setCharacterType( queryRandomItem( CharacterTypeEnum.values())); boy.setHasCar( queryRandomItem(BOOLEAN_ARRAY)); boy.setHasHouse( queryRandomItem(BOOLEAN_ARRAY)); boy.setRich( queryRandomItem(BOOLEAN_ARRAY)); return boy; } public static <T> T queryRandomItem(T[] itemArray) { return itemArray [RANDOM.nextInt(itemArray.length)]; }}
我们对外定义的接口如下:
public class RestService { public static Boy queryMyBoyInfo() { return RestClient.queryBoyDetail(); }}
问题分析
我们假如不同的相亲对象有不同的要求, A 女想要知道的是身高, 学历, 是否有钱, B 女想知道的时候是性格, 有房, 是否有钱,C 女想知道是否有钱
可以只返给用户需要的数据, 这样用户不会很困惑(找不到自己需要的字段或者对其他字段的含义疑惑), 也可以避免无用的数据传输的网络带宽, 降低网络传输耗时和提高传输的稳定性
部分响应模式
我们定义一个通用的支持部分响应模式
public class FieldJsonMapper { public static <T> String toJson (T t, String[] fields, Class<T> tClass) throws Exception{ StringBuilder json = new StringBuilder() .append("{"); for (int i = 0, fieldsLength = fields.length; i < fieldsLength; i++) { json.append(getString(t, tClass.getDeclaredField(fields[i]))); if (i != fieldsLength - 1) { json.append(","); } } json.append("}"); return json.toString(); } private static <T> String getString(T t, Field declaredField) throws IllegalAccessException Field declaredField) throws IllegalAccessException { declaredField.setAccessible(true); Object value = declaredField.get(t); if (declaredField.get(t) instanceof Integer) { return "\"" + declaredField.getName() + "\"" + ": " + value; } return "\"" + declaredField.getName() + "\"" + ": " + "\"" + value.toString() + "\""; }}
我们定义这样一个对外的接口:
public static String queryMyBoyInfoByFields(String[] fields) throws Exception { Boy boy = RestClient.queryBoyDetail(); return FieldJsonMapper.toJson (boy, fields, Boy.class);}
输出:
{"name": "贾宝玉","rich": "false","height": 180}
生产例子
我觉得这种模式主要用于给前端提供接口的时候使用, 当时重构做xx贷首页的时候, 业务不熟悉, 输出了很多无用的字段, 导致首页打开都有 10 s 的了, 现在想想这里面有我很大的"功劳", 听说现在 P8 + P7 墨在重写我的代码呢, 希望墨爷看我的代码可以学到我的设计模式(不骂我)
课后作业
-
谈谈部分响应模式的局限性和缺点
-
我们在生产上有一个用户信息接口, 返回值是:
@Datapublic class UserInfo { private String name; private Integer level; private LocalDateTime registerDate; private String email; private String phone;}
现在要求对外开一个包含姓名, 用户等级的接口
后言
设计模式参考地址: java-design-patterns.com/patterns/pa…
微信: