实现模式小结

Implementation Pattern

 

作者:Kent Beck,极限编程、测试驱动开发的推动者

 

软件业中最具创新力、最富盛名的领袖之一。长期以来,他一直致力于挑战软件工程教条,推动模式、测试驱动开发以及极限编程等思想的应用和传播。

 

XP就提倡两个人一起写同一段程序(PairProgramming),而且代码所有权是归于整个开发队伍(CollectiveCodeOwnership)。程序员在写程序和重整优化程序的时候,都要严格遵守编程规范。任何人都可以修改其他人写的程序,修改后要确定新程序能通过单元测试。结对编程的好处是,一个人编写代码时另一个人在思考。思考者的头脑中保持总体概念,不仅手头问题的这一段,而且还有XP指导方针。例如,如果两个人都在工作,就不太可能会有其中一个说“我不想首先写测试”而离开。如果编码者遇到障碍,他们就交换位置。如果两个人都遇到障碍,他们的讨论可能被在这个区域工作的其他人听到,可能给出帮助。这种结对方式,使事情顺畅、有章可循。也许更重要的是,他能使程序设计更具有社交性和娱乐性。

 

此书主要是关注:代码的可读性。

 

代码不仅是写给机器的,而对于庞大的工程,更是写给人看的,这样可维护性才比较强。

 

代码不仅要强调算法和性能,更要重构、可读性。

 

文章架构如下:

 

0、总括:价值观

1、类:为什么创建类、如何创建

2、行为(用代码表现逻辑、不同途径做同一件事)、状态(状态存取模式)

3、方法(方法的分解和命名)、容器(如何选择和使用容器)

 

 

模式:列举解决某一问题的各种方案,以及推荐方案的优点。帮助人们做决定。

 

一、类

 

1、对于重要的类,尽量用一个单词来命名

 

2、太长的类名读起来费劲,太短的类名又会考验读者的记忆力

 

3、接口名加I

 

4、函数式的风格:永远不会改变任何状态,只是创建新的值

     过程式的风格:过程的调用顺序至关重要

 

5、对于switch case的条件语句,可以把条件逻辑转换为消息,发送给子类或者委派,这样当条件频繁变化的时候,修改起来方便。

 

public void mouseDown(){
        switch(getTool()){
                case SELECTING:
                //do
                break;

                case CREATING_RECTANGLE:
                //do
                break;

                case EDITING_TEXT:
                //do
                break;

                default:
                break;
        }
}

如果要增加一种工具,则需要修改上面的代码

 

如果采用子类的方式来解决(swith的内容被分散到子类中,但是阅读代码的时候,需要逐个点开子类,才能看出逻辑来),则需要在运行时改变加载的工具,即需要编码确定运行时加载哪个类。

 

采用委派的话,则相对灵活一些。

 

public void mouseDown(){
        getTool().mouseDown();
}

 

委派对象可以保存在实例变量里面,也可以在使用的时候再决定。

 

一个常用的技巧,就是把发起委派的对象作为参数传递给接受委派的方法。

 

GraphicEditor
public void mouseDown(){
       this.mouseDown(this);
}

RectangleTool
public void mouseDown(GraphicEditor editor){
     editor.add(new RectangleFigure());
}

或者
private GraphEditor editor;
public RectangleTool(GraphicEditor editor){
     editor.add(new RectangleFigure());
}

 

可以把要调用的方法名保存在实例变量中,然后通过反射来调用该方法。

 

6、对于类库的静态方法

 

如果某些功能放在哪个对象中都不合适,那么就该把它们放在一个空类的静态方法中,任何人都不应该创建这个类的实例,它只是用来安放这些功能。

 

尽管类库相当常见,但是它不适合大量使用,把所有逻辑都放在静态方法中就错过了对象的最大好处:把数据放入私有命名空以便简化逻辑。应该尽量把类库编程合格的对象。

 

先把静态方法转变成实例方法,并由静态方法委派给实例方法,以维持接口不变。

 

public static void method(){
     new Library().instanceMethod();
}

private void instanceMethod(){

}

 

问题是这样的话,不就容易产生很多对象么?

http://blog.csdn.net/simonhe1973/article/details/3207373

 

 

二、状态

 

1、对于多个状态,可以保存在HashMap,然后按key来读取

 

2、对于局部变量,如果是收集器,比如集合的返回,一般命名为result或results

 

3、对于多次调用的话,可以先声明再复用

 

long now=System.currentTimeMillis();
for(Clock each:getClocks())
    each.setTime(now);

 

4、对于收集参数

 

如果是简单的返回值则

 

Node.java

int size(){
    int result=1;
    for(Node each:getChildren())
        result+=each.size();

    return result;
}

 

但如果合并并不是简单的相加,则传入一个参数收集结果将显得更加直观

 

Node.java

List asList(){
    List results=new ArrayList();
    addTo(results);
    return results;
}

void addTo(List elements){
     elements.add(getValue());
     for(Node each:getChildren())
          each.addTo(elements);
}

 

 

5、变长参数

 

print(String... args){

   for(String temp:args)

      System.out.println(temp);

}

 

调用的时候可以给出任意多个参数,例如:

 

print("hello");

print("hello","lisi");

print("hello","张三");

 

若有其他参数,变长参数放在最后

 

6、如果传入的参数过多,则应该考虑建个对象

 

7、变量的初始化

(1)及早初始化,一般声明的时候初始化,若不在声明时初始化,可以在构造器里面初始化

(2)延迟初始化,表示资源较为紧缺

 

三、行为

 

卫述句:一种表达简单和局部的异常状况的方式,它的影响后果完全是局部的。

 

原来:
void initialize(){
    if(!isInitialized()){
         //do....
    }
}

使用卫述句之后:
void initialize(){
     if(isInitialized())
         return;

      //do.......
}

 

第一个版本,当我们读到if的时候,总会想着去寻找else子句。

而使用卫述句之后,过程变得很简单,读起来不费劲。

 

卫述句更适合表达另外一种情况,即其中的一个控制流比其他的更重要。

 

当存在多个条件的时候,卫述句特别有用。

 

void compute(){
     Server server=getServer();
     if(server!=null){
           Client client=server.getClient();
            if(client!=null){
                 Request current=client.getRequest();
                 if(current!=null)
                      processRequest(current);
            }
     }
}

 

嵌套的条件语句孕育着错误。而改用卫述句之后,则不需要用到复杂的控制结构:

 

void compute(){
    Server server=getServer();
    if(server==null)
            return;

    Client client=server.getClient();
    if(client==null)
            return;

    Request current=client.getRequest();
    if(current==null)
            return;

    processRequest(current);
}

 

卫述句的一个变体就是在循环语句里面使用continue,它的意思是别管这个元素,继续下一个。

 

while(line=reader.readline()){
        if(line.startsWith('#'|| line.isEmpty())
             continue;
        //normal processing here
}

 

 

四、方法

 

1、反对使用大量小方法的理由是大量的方法调用会增加性能负担。流畅的代码更易于理解,而跳跃的抽象层次则破坏了代码的流畅性。但是方法体内代码太长的话,结构会不清晰。

 

2、方法的可见性:package,不想让包外的对象使用;protected,在向子类提供可重用的代码;private,当前类的私有方法。final,不介意别人使用,但不允许任何人改变它;static,不用实例化就能访问。静态方法可以被继承,但一旦覆盖就不能再调用超类的方法。静态方法的作用之一就是作为构造器的替代。

 

3、容器的访问方法,如果直接返回整个容器,则可能使得依赖于容器内容的内部状态在你不知情的情况下失效,为对象提供这样的万能接口等于错失了创建一个丰富而意图明确的接口机会。

 

可以在返回容器之前包装成一个不可修改的容器:

List<Book> getBooks(){

        return Collections.unmodifiableList(books);

}

 

不过这类包装只是在编译器前起作用,发生异常的时候,调试的代价大。

 

更好的方法是提供一些方法,为容器中的信息提供限制性的、意图明确的访问途径。

 

void addBook(Book arrival){
     books.add(arrival);
}

int bookCount(){
     return books.size();
}

Iterator getBooks(){
      return books.iterator();
}

List<Book> getBooks(){
       List<Book> result=new ArrayList<Book>();
        result.addAll(books);
     return results;
}

 

这样就防止了用户对容器的修改。

 

五、容器

 

214358_aStN_1383356.jpg

 

 

1、容器的第一个概念是大小,第二是元素是否有序,第三是元素的唯一性,第四是性能影响。

 

2、访问数组(elements[i])比类似的ArrayList操作(elements.get(i))要快10倍

 

3、使用容器应该关心的一点就是客户端是否需要修改它。一般可以在返回前包装到另外一个新容器中,或者自定义迭代方式。默认的Iterator是不安全的,因为可以调用remove等方法

 

4、到目前为止,编程中绝大多数容器都是用ArrayList,远远超过排名第二的HashSet

 

     ArrayList的contains以及所有依赖它的操作都有潜在的性能问题。

 

     HashSet、LinkedHashSet、TreeSet,三者中HashSet的速度最快,但是元素是无序的;LinkedHashSet按照元素被加入容器的顺序来对元素排序,但代价却是添加和删除元素要额外消耗30%的时间;TreeSet用Comparator来保持元素的顺序,不过在添加和删除元素或是检查元素是否存在的时候,所花的时间为logn,n为容器大小。

 

   如果要让元素具有稳定的顺序,可以用LinkedHashSet。


转载于:https://my.oschina.net/scipio/blog/263376

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值