程序设计的点点滴滴

  1. 一个函数最好不要超过30行,超过就分成几个子函数
  2. 尽量不要有多层循环(包括双层),每层循环可以放到不同的函数中,
    甚至内层循环体也可以放到一个函数中,提高程序可读性和可重用性。
foo() {
   for () {
       for () {
          do1;
          do2;
          do3;
        }
   }
}

可以改成,

foo() {
    for () {
        foo1();
    }
}

foo1() {
    for() {
        doSomething();
    }
}

doSomething() {
    do1;
    do2;
    do3;
}
  1. 如果一个比较heavy load(代价比较高)的方法,被调用多次,那么第一次调用的时候可考虑把结果缓存下来,以后直接用这个结果,而不需要多次调用该方法。
  2. 类和方法的参数,要尽可能是直接需要的参数,而不要传递大对象,只传递自己需要的对象。这样类和方法的输入才清晰,而且调用者也不必创建大对象,为哪些成员变量需要初始化而不知所措。
  3. 当你发现某些代码老是重复的写来写去,那么那些代码就很有可能能够抽象出来,放在公共函数里,变化的部分可以用模板方法来实现。
  4. 当一个类里面有一些函数根据同一个标志来进行不同的计算,那么这个类应该可以分为两个子类了。每个子类做一种计算,消除或减少if-else语句,提高可维护性。
  5. 最好不要用static方式初始化static变量,这会使单元测试很不方便。尽量不要写如下代码,
class MyClass {
     static YourClass yourClass;
     static {
           yourClass = new YourClass();
           yourClass.doSomething();
     }
}

把YourClass抽取出来设计成singleton。

  1. 在构造函数中启动线程要小心(尽可能不要这么做),线程启动的时候可能某些变量没有初始化,特别是需要子类初始化的变量。
  2. 使用daemon的线程要小心,main线程结束后daemon马上就结束,这样导致daemon线程正在处理的数据没有完成,比如数据没有保存或者提交。
  3. 如果有可能通过Class.newInstance()动态创建类实例,那么类的构造函数参数,要用java类,比如int要用Integer.
  4. 当有两个设计方案,无法取舍的时候,多想想:
    1. 哪个方案对外部的影响小: 包括外部模块,外部团队等.
    2. 哪个方案更容易测试.
    3. 哪个更容易控制,陷阱更少.
    4. 哪个方案改动的地方更集中.有时候一个方案看起来改动的多,但主要集中在一个地方,引入的风险反而更少. 而改动少,但改动面广的方案,反而风险高.
  5. get, create, build等创建对象的方法,应该是幂等的,内部不应该修改传入的对象参数的属性,或者静态全局对象属性.
  6. 谨慎的使用内部类,否则用Class.newInstance()实例化可能会失败.
  7. 业务与存储解耦
class AsinRevenueDataRevenueHelper {
      private static Map<String, Map<String, RevenueHBaseDaoChain>> realm2DaoMap = new ConcurrentHashMap<String, Map<String, RevenueHBaseDaoChain>>();
      public static AsinRevenueDataResult getAsinRevenueData(String asin, String realm, Date date) {
           ...
      }
      // 其余每个API也都是静态的
}

这个类相当于一个Singleton, 不太容易扩展. Realm相当于一个区域或者国家, 不管为哪个国家服务都是从HBase读取数据. 当我要将一部分国家的数据库迁移到DynamoDB时,
这个类就不能用了. 我只好修改这个设计, 如下:

public interface AsinRevenueDataRevenueHelperInterface {
         public AsinRevenueDataResult getAsinRevenueData(String asin, Date date);   
         // 其它接口
         ...    
}

基于这个接口有两个实现.

// 从HBase获取数据接口实现类
public class AsinRevenueDataHBaseHelper implements AsinRevenueDataRevenueHelperInterface {
          private String realm;
          public AsinRevenueDataHBaseHelper(String realm) {
               this.realm = realm;
          }
          @Override
          public AsinRevenueDataResult getAsinRevenueData(String asin, Date date) {
               ...
          }
          // 其它override的APIs 
}
// 从DynamoDB获取数据接口实现类
public class AsinRevenueDataDynamoDBHelper implements AsinRevenueDataRevenueHelperInterface {
          private String realm;
          public AsinRevenueDataDynamoDBHelper(String realm) {
               this.realm = realm;
          }
          
          @Override
          public AsinRevenueDataResult getAsinRevenueData(String asin, Date date) {
               ...
          }
          // 其它override的APIs          
}

数据接口工厂类:

public class AsinRevenueDataHelperProvider {
        private static Map<String, AsinRevenueDataHelperInterface> realm2AsinRevenueDataHelperMap = new HashMap<String, AsinRevenueDataHelperInterface>();
        private static boolean initialized = false;

 public static AsinRevenueDataHelperInterface getAsinRevenueDataHelper(String realm) {
           if (!initialized) {
               initialize();
           } 
           return realm2AsinRevenueDataHelperMap.get(realm); 
}

public static synchronized initialize() {
           if (initialized) return;
           // 对每个国家初始化数据接口类
           for each realm in realms {
               if (isDynamoDBEnabled(realm)) {
                   realm2AsinRevenueDataHelperMap.put(realm, new AsinRevenueDataDynamoDBHelper());
               } else {
                   realm2AsinRevenueDataHelperMap.put(realm, new AsinRevenueDataHBaseHelper());
               }
           }
           initiazlied = true;
        }
 }

修改完设计后,Client端只需要跟接口(AsinRevenueDataHelperInterface)以及工厂类(AsinRevenueDataHelperProvider)耦合,不需要知道具体存储介质.

  1. 在设计一个新系统的时候,一开始不要想得很全很大,而是先从一个最简单的最common的use case入手,画出这个use case的流程图,甚至编写伪代码,挖掘其中的概念,遵循面向对象高内聚低耦合的策略,一步一步迭代,逐渐形成概念模型。
  2. 原有业务逻辑是 C = getA(),当我们的业务逻辑改成 C = max(getA(), getB())时,如果getB()的内部有异常,应当抛出异常,而不是返回0,不要假设getB()出现异常,就用getA()。既然增加了B,而B出现异常的话,就要显式的抛出异常好了,当然没有B则可以fallback到A(没有B和B出现异常是两回事。)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值