《CC》的感后感

其实以前在zte,当时团队一堆人再争着看CC,自己并没有当回事儿,因为当时觉得,还是先把技术功底打好,多学多写代码。然而,自己忽视了代码的本质。没错,代码的本质是可阅读性。还好,不晚

零、一个简单的范例

 // 求二叉树的深度
 // 递归解法:
 // (1)如果二叉树为空,二叉树的深度为0
 // (2)如果二叉树不为空,二叉树的深度 = max(左子树深度, 右子树深度) + 1
public int depth(node node) {
  if (node == null) // 递归出口
   return 0;
  int l = depth(node.lchild);
  int r = depth(node.rchild);
  return l > r ? (l + 1) : (r + 1);
};

思考:我们有什么不对?

一、起始的一些思想

1、代码是根,无法丢弃
2、烂代码毁灭一切
3、勒布朗法则:稍后等于永不()
4、要保持专业精神
5、读与写的比例:10:1 (
)
6、“让营地比你来时更干净”
7、六大原则:SRP、OCP、DIP、LOD、LSP、ISP、

二、命名

public List<int[]> getThem(){
    List<int[]> list1 = new ArrayList<>();
    for(int i = 0; i<theList.size(); i++)
        if(theList.get(i)[0]==4)
            list1.add(theList.get(i));
    return list1;
}

思考:这会影响什么?

1、准确的命名
2、最好不要用List的后缀(或者是平台的专用词,工具类名)
3、getAccount()与getAccountInfo()的问题
4、可读性、课所搜性
5、Java无需要在名称中记住变量类型
6、尽量不要用i,j,k这些个变量进行循环计数
7、类名应该为名词或者是名词短语(Account)
方法名应该为动词或者是动词短语
8、add(a、增加新东西;b、连接现有的两个词),这种一语双关的命名就不是很好

@Data
class PersonInfo{

    private String firstName;

    private String lastName;

    private String phone;

    private String street;

    private String city;

    private String state;

}

public class UserHandle{
    ......

    public void handle(PersonInfo p){
        ......
        String s = p.getState();
        ......
    }
    ......
}

思考:getState是什么Gui?

9、添加有意义的语境,例如,再firstName、street、city、state这样的一组词汇里面,大概知道我们 要表达的是地址,可是单独吧state这个单词拿出来,并不知道我们是要说的地址相关

三、函数

public Money calculatePay(Employee e) throws Exception{
    switch(e.type){
        case COMMISSIONED:
            return calculateCommissionedPay(e);
        case HOURLY:
            return calsulateHourlyPay(e);
        default:
            throw new InvalidEmployee(e.type);
    }
}

思考:这个违反了什么原则?

1、尽量短小
2、只做一件事情(同一抽象层级上面)
3、switch尽量在较低的抽象层级,且永远不重复(向下规则:从上面向下面读代码)

private PlainResult<Boolean> updateStockWhenTakeupProduct(GoodsOperateParam goodsOperateParam) {
    ......
        //对多个门店上架时候设置库存
        logger.info("stock setting:kdtId:{};deptId:{};goodsId:{}", kdtId, deptId, goodsId);
        List<GoodsOperateParam.DeptStockParam> stockList = stockMap.get(deptId);
        //更新每个商品的sku库存
        List<GoodsItemSkuDO> goodsItemSkuDOS = getGoodsItemSkuDOS(kdtId, deptId, goodsId, stockList);//1
        logger.info("goods item sku database param:{}", JsonUtils.toJson(goodsItemSkuDOS));
        goodsItemSkuDAO.updateBatch(kdtId, deptId, goodsId, goodsItemSkuDOS);
        //这里要设置商品总库存,就是设置分店中商品每个goodsId对应的库存总量,这个库存是对应每个sku库存相加所得
        List<GoodsQuery.GoodsIdStock> sumStock = getSumStock(goodsId, stockList);//2
        logger.info("dept service item database param:{}", JsonUtils.toJson(sumStock));
        deptServiceItemDAO.batchUpdateDisplayStatusAndStock(kdtId, deptId, goodsOperateParam.getShelveType(), sumStock);

        synESDeptStock(deptId, sumStock, goodsId, goodsOperateParam);//3
    ......

    }

private List<GoodsItemSkuDO> getGoodsItemSkuDOS(Long kdtId, Long deptId, Long goodsId, List<GoodsOperateParam.DeptStockParam> stockList) {
        List<GoodsItemSkuDO> result = new ArrayList<>();

    stockList.forEach(skuStock -> {
        GoodsItemSkuDO goodsItemSkuDO = new GoodsItemSkuDO();
        goodsItemSkuDO.setDeptId(deptId);
        goodsItemSkuDO.setKdtId(kdtId);
        goodsItemSkuDO.setGoodsId(goodsId);
        goodsItemSkuDO.setSkuId(skuStock.getSkuId());
        goodsItemSkuDO.setTotalStock(skuStock.getStockNum().intValue());
        result.add(goodsItemSkuDO);
    });


    return result;
}

private List<GoodsQuery.GoodsIdStock> getSumStock(Long goodsId, List<GoodsOperateParam.DeptStockParam> goodsStockParamList) {
    List<GoodsQuery.GoodsIdStock> sumStock = new ArrayList<>();
    Long sum = goodsStockParamList.stream().map(GoodsOperateParam.DeptStockParam::getStockNum).reduce(0L, Long::sum);
    GoodsQuery.GoodsIdStock goodsIdStock = new GoodsQuery.GoodsIdStock(goodsId, sum);
    sumStock.add(goodsIdStock);
    return sumStock;
}

private void synESDeptStock(Long deptId, List<GoodsQuery.GoodsIdStock> sumStock, Long goodsId, GoodsOperateParam goodsOperateParam) {
    EsGoodsDTO esGoodsDTO = new EsGoodsDTO();
    esGoodsDTO.setDeptId(deptId);
    esGoodsDTO.setGoodsId(goodsId);
    esGoodsDTO.setOnShelve(goodsOperateParam.getShelveType());
    esGoodsDTO.setStockNum(sumStock.get(0).getStockNum()); //@author jicheng 这里面细节进行统计每一个sku的库存累加,所以不用乘以所有sku的size了
    if (esGoodsDTO.getStockNum() > 0) {
        esGoodsDTO.setSoldStatus(GoodsStatusEnum.ON_SHELVE.getValue());
    }
    esGoodsManager.update(esGoodsDTO);
}

思考:还有什么改进点?请指教

4、尽量少入参
5、无副作用(不能即检查密码的正确性,又初始化系统)
6、避免使用输出参数
7、使用异常代替返回错误码
8、把trycatch主题抽出成为一个函数
9、代码尽量不要重复
10、一步步来改进代码,好代码不是一次就能成就的

......
    //重复了 暂时还没考虑抽查来
    if (plainResult.isSuccess()) {
        PlainResult<Long> updateResult = null;
        if (inputServingItemParam.getDeptId() == MultiShopType.HEADQUARTERS.deptId()) {
            updateResult = goodsManager.updateServingItemV2(inputServingItemParam, serviceItemDOResult.getData());
        }else {
            updateResult = deptServiceItemManager.updateDept(inputServingItemParam);
        }
        plainResult.setCode(updateResult.getCode());
        plainResult.setMessage(updateResult.getMessage());
        plainResult.setSuccess(updateResult.isSuccess());
        plainResult.getData().setId(updateResult.getData());
    }
    if (plainResult.isSuccess()) {
        PlainResult<Boolean> updateDBResult = null;
        if (inputServingItemParam.getDeptId() == MultiShopType.HEADQUARTERS.deptId()) {
            updateDBResult = goodsManager.updateHeadGoodsInDB(inputServingItemParam);
        }else {
            updateDBResult = goodsManager.updateDeptGoodsInDB(inputServingItemParam);
        }
        if (!updateDBResult.isSuccess()) {
            logger.warn("update service error,param={}", JsonUtils.toJson(inputServingItemParam));
        }
    }
......

思考:前人种树后人乘凉?

四、注释

    // find out if gift already given 
    boolean isAlreadyGiven = false; 
    for (String given : giftsGiven) { //this is a loop
        if(gift.equals(given)){ 
            isAlreadyGiven = true; 
            break; 
        } 
    }

    private boolean isGiftNotAlreadyGiven(String gift) {
        boolean isAlreadyGiven = true;
        for (String given : giftsGiven) {
            if(gift.equals(given)){
                isAlreadyGiven = false;
                break;
            }
        }
        return isAlreadyGiven;
    }

思考:什么情况下我们需要注释?

1、不要因为代码乱而写注释,首先要改好老代码!
2、最好用代码的优秀的命名来取代注释
3、使用注释的场景罗列:法律信息、提供信息注释、对意图的解释或是阐释、警示作用、TODO注释
4、不好的注释:可怕的废话、类方法上的Javadoc注释、注释掉的代码、HTML注释、很多注释(信息过多 )、位置标记、显而易见的说明

/**
 * Created by zhaoyifei.
 *
 * Time 2017/3/2 01:23
 *
 * Desc Goods对外的接口
 */
public interface  GoodsService {

......


    /**
     * 总店 商品上下架, 支持批量, 以这个接口为基础,要拓展两个接口
     * @update jicheng
     * @author hunterPan
     */
    PlainResult<GoodsOperationResultDTO> batchOperateByItemId(GoodsOperateParam goodsOperateParam);

......

}

思考:以上的接口注释好吗?

五、格式

1、短文件比长文件易于理解
2、垂直方向上的间隔(主要表现在方法之间、属性之间、还有逻辑之间)
3、少用protected,因为使用到的变量和方法尽量要靠近
4、调用与被调用的函数尽量要靠近
5、水平宽度建议:120字符(刚好一瓶)
6、遵守团队规则

六、对象与数据结构

1、解释两个名词:

  • 对象:将数据隐藏于抽象之后,暴露操作数据的函数。
  • 数据结构:暴露数据本身,不提供有意义的函数。
/*
Law of Demeter
定义:一个对象应该对其他对象保持最少的了解。
问题由来:类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。
解决方案:尽量降低类与类之间的耦合。
解释:德米忒耳定律有一个更简单的定义:只与直接的朋友通信。首先来解释一下什么是直接的朋友:每个对
象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的
方式很多,依赖、关联、组合、聚合等。其中,我们称出现成员变量、方法参数、方法返回值中的类为直接的
朋友,而出现在局部变量中的类则不是直接的朋友。也就是说,陌生的类最好不要作为局部变量的形式出现在
类的内部。
*/
//违反的案例

class CompanyManager{  
  
    public List<Employee> getAllEmployee(){  
        List<Employee> list = new ArrayList<Employee>();  
        for(int i=0; i<30; i++){  
            Employee emp = new Employee();  
            //为总公司人员按顺序分配一个ID  
            emp.setId("总公司"+i);  
            list.add(emp);  
        }  
        return list;  
    }  
      
    public void printAllEmployee(SubCompanyManager sub){  
        List<SubEmployee> list1 = sub.getAllEmployee();  
        for(SubEmployee e:list1){  
            System.out.println(e.getId());  
        }  
  
        List<Employee> list2 = this.getAllEmployee();  
        for(Employee e:list2){  
            System.out.println(e.getId());  
        }  
    }  
} 

//符合的案例
class CompanyManager{  
    public List<Employee> getAllEmployee(){  
        List<Employee> list = new ArrayList<Employee>();  
        for(int i=0; i<30; i++){  
            Employee emp = new Employee();  
            //为总公司人员按顺序分配一个ID  
            emp.setId("总公司"+i);  
            list.add(emp);  
        }  
        return list;  
    }  
      
    public void printAllEmployee(SubCompanyManager sub){  
        sub.printEmployee();  
        List<Employee> list2 = this.getAllEmployee();  
        for(Employee e:list2){  
            System.out.println(e.getId());  
        }  
    }  
} 

思考:返回值“好吗”?

2、Demeter定律(德米忒耳定律):类C的方法f,应该调用一下对象方法:(*)

  • C
  • 由f创建的对象
  • 作为参数传递给f的对象
  • 由C的实体变量持有的对象
final String filedir = ctct.getOptions().getScratchDir().getAbsolutePath();
/*
如果上面的ctct是一个对象就会违反Demeter定律,如果是一个数据结构就不会违反
属性访问就不会涉及到Demeter定律的问题
*/

七、错误处理

1、返回错误码的方式不是很好、尽量抛异常
2、尽量使用不可控异常(运行时异常),因为如果函数改成可控异常,那么调用者都要修改,必然会违反OCP
3、将较多的异常类型捕获,放到脚底的抽象层次里面
4、以下的代码有点不好,不要在异常处理块中做业务逻辑

    try{
        MealExpenses ex = experseReportDao.getMeals(employee.getId());
        mTotal += ex.getTotal();
    }catch(Exception e){
        mTotal += getMealPerDiem();
    }

6、不要返回null,并且不要传递null

八、边界

//前世
Map sensors = new HashMap();
dealSensors(sensors);

//今生
Sensors sensors = new Sensors();
dealSensors(sensors);

class Sensors{
    private Map<Long,String> sensorContainer = new HashMap<>();

    public Sensor getSensor(Long id){
        return sensorContainer.get(id);
    }
}

思考:什么是较低的抽象?

1、尽量不要将公共API当参数进行传递,有节制的进行封装
2、学习性测试(第三方的库)

九、单测

1、TDD流程

  • 先不写生产代码
  • 编写刚好不通过的单测
  • 编写使得单测刚好通过的生产代码

2、测试代码与生产代码同等重要
3、测试代码非常关键的一点在于:可读性
4、每个测试尽量使用一个断言
5、FIRST(快速、独立、可重复、自足验证、及时)

十、类

public class Stack{
    private int topOfStack = 0;
    List<Integer> elements = new LinkedList<>();

    public int size(){
        return topOfStack;
    }

    public void push(int element){
        topOfStack++;
        elements.add(element);
    }

    public int pop() throws PopedWhenEmpty{
        if(topOfStack == 0){
            throw new PopedWhenEmpty();
        }
        int element = elements.get(--topOfStack);
        elements.remove(topOfStack);
        return element;
    }
}

思考:以上代码满足了什么原则?

1、类应该短小,权责尽量的少
2、内聚、SRP、OCP
3、大函数拆分成小函数,往往是拆分为多个小类的时机

十一、系统

1、构造与使用分开(DIP、工厂)
2、关注切面(AOP)
3、消除紧耦合

十二、逐步改进

1、自上而下重要点

  • 运行所有的测试
  • 不可重复
  • 表达程序员的意图
  • 尽可能减少类和方法的数量

十三、一些其余的点



enum CheckType{
    INTEGER(0, "integer"),
    STRING(1, "string");

    private int value;

    private String name;
}

interface Checker{
    void check();
}
class IntegerChecker implements Checker{
    public void check(){}
}

class StringChecker implements Checker{
    public void check(){}
}

public CheckerHandler{

    private static Map<Integer,Checker> checkers = new HashMap(){{
        put(CheckType.INTEGER.getValue(),(Checker)(new IntegerChecker()));
        put(CheckType.STRING.getValue(),(Checker)(new StringChecker()));
    }};

    public void beginToCheck(){
        for(CheckType checkerType : CheckType.values()){
            Checker checker = checkers.get(checkerType.getValue());
            checker.check();
        }
    }
}

思考:还有什么方式取代if/else,switch/case

1、用改对象的状态来代替输出参数
2、干掉死函数
3、用多态代替ifelse与switch
4、用命名常量代替魔法数字
5、避免用否定条件
6、掩蔽时序性耦合
7、函数应该只在一个抽象层级上面
8、较高层级放可配置的数据,用参数向下传递
9、使用枚举

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353