25、软件开发工具集成与代码优化技巧

软件开发工具集成与代码优化技巧

1. 开发工具集成的优势

在软件开发过程中,合理集成各种工具能显著提升开发效率和质量。例如,将 Mylyn 与 Bugzilla 集成,能带来诸多好处,而且这种集成的优势还能延伸到其他开发工具的整合中。

1.1 任务窗口上下文区域的优势

在任务窗口的上下文区域,如果与代码仓库(如 Subversion (SVN) 或 CVS)集成,就能在问题的过滤视图(上下文)中查看和管理整个仓库的交互。以 Zend Studio 为例,当有文件被修改并保存到本地时,在上下文窗口中会有明显标识,如“>”。这使得开发者能更清晰地了解任务和项目中哪些文件需要提交到仓库。不过,这种文件跟踪功能只有在 Mylyn 中激活问题时才能使用。

1.2 代码审查时的优势

在代码审查过程中,也能体现出集成的好处。可以从问题的上下文中访问简短列表,通过右键单击项目(或单个文件),就能将自己的工作与代码仓库中的最新版本(修订号)进行比较。还可以查看仓库中更早期的提交历史(修订号)。这种代码变更审查也可以在本地保存文件的历史记录中进行,具体选择取决于开发者提交工作到仓库的频率。

以下是相关工具的资源链接:
- Zend Studio for Eclipse 主页:www.zend.com/en/products/studio/
- Bugzilla 主页:www.bugzilla.org/
- Mylyn Eclipse 页面:www.eclipse.org/mylyn/
- Mylyn 主页:http://tasktop.com/mylyn/
- 视频:Dr. Mik Kersten 的 W - JAX 2008 主题演讲:Redefining the “i” of IDE:http://tasktop.com/videos/w - jax/kersten - keynote.html

2. 代码优化的三种技术

为了实现稳定、高质量的代码库,减少调试时间并便于频繁发布,开发者可以采用三种技术:重构、单元测试和持续集成。

2.1 重构

重构是一种修改代码结构以提高其质量的方法。重构时,并非要添加或修改功能,而是编程中必要且自然的一部分。但需要注意的是,重构时很容易在不经意间修改功能,由此引入的错误很难发现,可能会长期潜伏,直到有人注意到。

常见的重构示例包括:
- 消除重复代码,创建新函数来替代。
- 用简化的语句或描述性的函数名替换复杂的逻辑表达式,以提高代码可读性。
- 从大型类中提取方法并移动到新的或更合适的类中。
- 减少多层控制结构(如 if/else、for、foreach、while、switch)的嵌套。
- 进行面向对象的设计更改,如扩展基类或使用构建器或单例等设计模式。

以下是一些重构的代码示例:

示例 1:判断是否去散步的代码
<?php
define('OWN_A_DOG', true);
define('TIRED', false);
define('HAVE_NOT_WALKED_FOR_DAYS', false);
define('NICE_OUTSIDE', false);
define('BORED', true);

if ( (OWN_A_DOG && (!TIRED || HAVE_NOT_WALKED_FOR_DAYS)) || (NICE_OUTSIDE && !TIRED) || BORED )
{
    goForAWalk();
}

function goForAWalk() {
    echo "Going for a walk";
}
?>

经过重构后:

<?php
require_once('walkConfig.php');

if ( (OWN_A_DOG && (!TIRED || HAVE_NOT_WALKED_FOR_DAYS)) || (NICE_OUTSIDE && !TIRED) || BORED )
{
    goForAWalk();
}

function goForAWalk() {
  echo "Going for a walk";
}

?>

配置文件 walkConfig.php 内容如下:

<?php
define('OWN_A_DOG', true);
define('TIRED', false);
define('HAVE_NOT_WALKED_FOR_DAYS', false);
define('NICE_OUTSIDE', false);
define('BORED', true);
?>

还可以进一步将逻辑表达式提取到函数中,提高可读性:

<?php
require_once('walkConfig.php');

if (shouldWalk()) {
    goForAWalk();
}

function shouldWalk() {
    return ( (OWN_A_DOG && (!TIRED || HAVE_NOT_WALKED_FOR_DAYS)) ||  
             (NICE_OUTSIDE && !TIRED) ||  
             BORED);
}

function goForAWalk() {
  echo "Going for a walk";
}

?>

甚至可以继续拆分逻辑:

<?php
require_once('walkConfig.php');

if (shouldWalk()) {
    goForAWalk();
}

function shouldWalk() {
    return ( timeToWalkTheDog() || feelLikeWalking() );
}

function timeToWalkTheDog() {
   return (OWN_A_DOG && (!TIRED || HAVE_NOT_WALKED_FOR_DAYS));
}

function feelLikeWalking() {
   return ((NICE_OUTSIDE && !TIRED) || BORED);
}

function goForAWalk() {
  echo "Going for a walk";
}

?>
示例 2:消除重复代码的 PHP 脚本
<?php

$total = 0;
$value = rand(1, 10);
if ($value > 5) {
    $multiple = 2;
    $total  = $value;
    $total *= $multiple;
    $total += (10 - $value);
    print "goodbye<br/>";
    print "initial value is $value<br/>";
    print "the total is $total<br/>";
} else {
    $multiple = 7;
    $total  = $value;
    $total *= $multiple;
    $total += (10 - $value);
    print "hello!<br/>";
    print "initial value is $value<br/>";
    print "the total is $total<br/>";
}
?>

重构后:

<?php

$total = 0;
$value = rand(1, 10);
if ($value > 5) {
    $total = changeTotalValue($value, 2);
    displayMessage("goodbye", $value, $total);
} else {
    $total = changeTotalValue($value, 7);
    displayMessage("goodbye", $value, $total);
}

function changeTotalValue($value, $multiple){
    $total = $value * $multiple;
    $total += (10 - $value);
    return $total;
}

function displayMessage($greeting, $value,$total){
    print "$greeting<br/>";
    print "initial value is $value<br/>";
    print "the total is $total<br/>";
}
?>

2.2 单元测试

单元测试有助于确保重构引入的意外影响能立即被发现。当单元测试失败时,需要检查失败的原因。如果是因为有意调整了功能而预期失败,只需调整测试直到通过;但如果是意外失败,则需要修复代码中新引入的错误。

2.3 持续集成

持续集成(CI)在整个项目中执行质量保证(QA)。团队成员每天(甚至更频繁)将代码更改集成到项目的源代码控制仓库中。CI 服务器会轮询仓库中的更改,在检测到代码更改后、按固定时间间隔或按需执行自动“构建”。构建过程会执行诸如运行单元测试和静态分析等任务。持续集成的频繁、自动特性能迅速提醒开发者代码中导致“构建失败”的更改。在像 PHP 这样的非编译语言中,这通常指一个或多个单元测试失败。使用 CI 能让开发者对代码库更有信心,从而实现更频繁、更稳定的发布,还能运行构建脚本,执行一系列命令和任务,避免重复、枯燥、耗时且容易出错的工作。

以下是一个 mermaid 流程图,展示持续集成的基本流程:

graph LR
    A[代码更改] --> B[提交到仓库]
    B --> C{CI 服务器检测到更改}
    C -->|是| D[执行自动构建]
    D --> E[运行单元测试]
    E --> F[静态分析]
    F --> G{构建是否成功}
    G -->|是| H[发布新版本]
    G -->|否| I[通知开发者修复问题]
    C -->|否| B

总之,合理运用开发工具集成和代码优化技术,能有效提升软件开发的效率和质量。开发者可以根据实际情况选择合适的工具和技术,不断优化代码,提高项目的稳定性和可维护性。

3. 大型遗留代码的重构实践

大型遗留代码往往结构复杂、缺乏测试,给开发和维护带来很大挑战。下面通过一个具体的例子,展示如何对大型遗留代码进行重构。

3.1 初始代码分析

以下是一个计算从源位置到目标位置的最佳出行方式并显示总行程时间的代码:

<?php

error_reporting ( E_ALL );
//constants
define ( 'WALK_STEP', 0.25 ); //quarter meter steps
define ( 'BIKE_STEP', 3.00 ); //three meter steps
define ( 'BUS_STEP', 30.00 ); //bus steps
define ( 'BUS_DELAY', 300 ); //five minutes to wait for bus
define ( 'CAR_STEP', 50.00 ); //car steps
define ( 'CAR_DELAY', 20 ); //twenty seconds to get car up to speed
define ( 'HAS_CAR', true );
define ( 'HAS_MONEY', true );
define ( 'IN_A_RUSH', true );
define ( 'ON_BUS_ROUTE', true );
define ( 'HAS_BIKE', false );
define ( 'STORMY_WEATHER', false );
define ( 'WALKING_MAX_DISTANCE', 2500 );

class Location {

    public $x = 0;
    public $y = 0;

    public function __construct($x, $y) {
        $this->x = $x;
        $this->y = $y;
    }

    public function toString() {
        return "(" . round ( $this->x, 2 ) . ",  " . round ( $this->y, 2 ) . ")";
    }
}

function travel(Location $src, Location $dest) {

    //calculate the direction vector
    $distance_y = $dest->y - $src->y;
    $distance_x = $dest->x - $src->x;
    $angle = null;

    if ($distance_x) {
        if ($distance_y) {
            $angle = atan($distance_y / $distance_x);
        } else {
            if ($distance_x > 0) {
                $angle = 0.0; //right
            } else {
                $angle = 180.0; //left
            }
        }
    } else {
        if ($distance_y) {
            if ($distance_y < 0) {
                $angle = - 90.0;    //down
            } else {
                $angle = 90.0;      //up
            }
        }
    }

    $angle_in_radians = deg2rad ( $angle );

    $distance = 0.0;
    //calculate the straight line distance
    if ($dest->y == $src->y) {
        $distance = $dest->x - $src->x;
    } else if ($dest->x == $src->x) {
        $distance = $dest->y - $src->y;
    } else {
        $distance = sqrt ( ($distance_x * $distance_x) +  
           ($distance_y * $distance_y) );
    }

    print "Trying to go from " . $src->toString () .  
                  " to " . $dest->toString () . "<br/>\n";
    if (IN_A_RUSH) {
        print "<strong>In a rush</strong><br/>\n";
    }
    print "Distance is " . $distance . " in the direction of " .  
                   $angle . " degrees<br/>";

    $time = 0.0;

    $has_options = false;
    if (HAS_CAR || (HAS_MONEY && ON_BUS_ROUTE) || HAS_BIKE) {
        $has_options = true;
    }

    if ($has_options) {
        if (STORMY_WEATHER) {
            if (HAS_CAR) {
                //drive
                while ( abs ( $src->x - $dest->x ) > CAR_STEP ||
                                        abs ( $src->y - $dest->y ) > CAR_STEP ) {
                    $src->x += (CAR_STEP * cos ( $angle_in_radians ));
                    $src->y += (CAR_STEP * sin ( $angle_in_radians ));
                    ++ $time;
                    print "driving a car... currently at (" .  
                                                  round ( $src->x, 2 ) . ",  " .  
                                                  round ( $src->y, 2 ) . ")<br/>\n";
                }
                print "Got to destination by driving a car<br/>";
            } else if (HAS_MONEY && ON_BUS_ROUTE) {
                //take the bus
                while ( abs ( $src->x - $dest->x ) > BUS_STEP ||  
                                        abs ( $src->y - $dest->y ) > BUS_STEP ) {
                    $src->x += (BUS_STEP * cos ( $angle_in_radians ));
                    $src->y += (BUS_STEP * sin ( $angle_in_radians ));
                    ++ $time;
                    print "on the bus... currently at (" .  
                                                   round ( $src->x, 2 ) . ",  " .  
                                                   round ( $src->y, 2 ) . ")<br/>\n";
                }
                print "Got to destination by riding the bus<br/>";
            } else {
                //ride bike
                while ( abs ( $src->x - $dest->x ) > BIKE_STEP ||  
                                         abs ( $src->y - $dest->y ) > BIKE_STEP ) {
                    $src->x += (BIKE_STEP * cos ( $angle_in_radians ));
                    $src->y += (BIKE_STEP * sin ( $angle_in_radians ));
                    ++ $time;
                    print "biking... currently at (" .  
                                                   round ( $src->x, 2 ) . ",  " .  
                                                   round ( $src->y, 2 ) . ")<br/>\n";
                }
                print "Got to destination by biking<br/>";
            }
        } else {
            if ($distance < WALKING_MAX_DISTANCE && ! IN_A_RUSH) { //walk
                while ( abs ( $src->x - $dest->x ) > WALK_STEP ||  
                                        abs ( $src->y - $dest->y ) > WALK_STEP ) {
                    $src->x += (WALK_STEP * cos ( $angle_in_radians ));
                    $src->y += (WALK_STEP * sin ( $angle_in_radians ));
                    ++ $time;
                    print "walking... currently at (" .  
                                                   round ( $src->x, 2 ) . ",  " .  
                                                   round ( $src->y, 2 ) . ")<br/>\n";
                }
                print "Got to destination by walking<br/>";
            } else {
                if (HAS_CAR) {
                    //drive
                    $time += CAR_DELAY;
                    while ( abs ( $src->x - $dest->x ) > CAR_STEP ||  
                                                    abs ( $src->y - $dest->y ) > CAR_STEP ) {
                        $src->x += (CAR_STEP *  
                                                            cos ( $angle_in_radians ));
                        $src->y += (CAR_STEP *  
                                                            sin ( $angle_in_radians ));
                        ++ $time;
                        print "driving a car... currently at (" .     
                                                               round ( $src->x, 2 ) . ",  " .  
                                                               round ( $src->y, 2 ) . ")<br/>\n";
                    }
                    print "Got to destination by driving a car<br/>";
                } else if (HAS_MONEY && ON_BUS_ROUTE) {
                    //take the bus
                    $time += BUS_DELAY;
                    while ( abs ( $src->x - $dest->x ) > BUS_STEP ||  
                                                    abs ( $src->y - $dest->y ) > BUS_STEP ) {
                        $src->x += (BUS_STEP *  
                                                            cos ( $angle_in_radians ));
                        $src->y += (BUS_STEP *  
                                                            sin ( $angle_in_radians ));
                        ++ $time;
                        print "on the bus... currently at (" . 
                                                              round ( $src->x, 2 ) . ",  " .  
                                                              round ( $src->y, 2 ) . ")<br/>\n";
                    }
                    print "Got to destination by riding the bus<br/>";
                } else {
                    //ride bike
                    while ( abs ( $src->x - $dest->x ) > BIKE_STEP ||     
                                                    abs ( $src->y - $dest->y ) > BIKE_STEP ) {
                        $src->x += (BIKE_STEP *  
                                                            cos ( $angle_in_radians ));
                        $src->y += (BIKE_STEP *  
                                                            sin ( $angle_in_radians ));
                        ++ $time;
                        print "biking... currently at (" .  
                                                               round ( $src->x, 2 ) . ",  " .  
                                                               round ( $src->y, 2 ) . ")<br/>\n";
                    }
                    print "Got to destination by biking<br/>";
                }
            }
        }
    } else {
        if (STORMY_WEATHER) {
            print "ERROR: Storming<br/>";
        } else if ($distance < WALKING_MAX_DISTANCE) {
            //walk
            while ( abs ( $src->x - $dest->x ) > WALK_STEP ||  
                                abs ( $src->y - $dest->y ) > WALK_STEP ) {
                $src->x += (WALK_STEP * cos ( $angle_in_radians ));
                $src->y += (WALK_STEP * sin ( $angle_in_radians ));
                ++ $time;
                print "walking... currently at (" .  
                                      round ( $src->x, 2 ) . ",  " .  
                                      round ( $src->y, 2 ) . ")<br/>\n";
            }
            print "Got to destination by walking<br/>";
        } else {
            print "ERROR: Too far to walk<br/>";
        }
    }

    print "Total time was: " . date ( "i:s", $time );
}
//sample usage
//travel ( new Location ( 1, 3 ), new Location ( 4, 10 ) );
?>

这段代码存在诸多问题:
- 代码是一个单一的大型函数,承担了过多职责,包括计算距离、确定出行方式和导航等。
- 嵌套层级过多,添加测试非常困难。
- 存在大量重复代码,且缺乏全局变量的管理。
- 没有任何测试,难以确保代码的准确性和可靠性。

3.2 第一轮重构

3.2.1 提取常量和类

将常量提取到 config.php 文件中:

<?php
define ( 'WALK_STEP', 0.25 ); //quarter meter steps
define ( 'BIKE_STEP', 3.00 ); //three meter steps
define ( 'BUS_STEP', 30.00 ); //bus steps
define ( 'BUS_DELAY', 300 ); //five minutes to wait for bus
define ( 'CAR_STEP', 50.00 ); //car steps
define ( 'CAR_DELAY', 20 ); //twenty seconds to get car up to speed
define ( 'HAS_CAR', true );
define ( 'HAS_MONEY', true );
define ( 'IN_A_RUSH', true );
define ( 'ON_BUS_ROUTE', true );
define ( 'HAS_BIKE', false );
define ( 'STORMY_WEATHER', false );
define ( 'WALKING_MAX_DISTANCE', 2500 );
?>

Location 类提取到 location.php 文件中:

<?php

class Location {

    public $x = 0;
    public $y = 0;

    public function __construct($x, $y) {
        $this->x = $x;
        $this->y = $y;
    }

    public function toString() {
        return "(" . round($this->x, 2) . ",  " . round($this->y, 2) . ")";
    }

}

?>
3.2.2 提取视图逻辑

将显示出行路径信息的代码提取到 travelView.php 文件中的 TravelView 类中:

<?php

error_reporting(E_ALL);
require_once ('config.php');
require_once ('location.php');

class TravelView {

    public static function displayOurIntendedPath( $angle, $distance,  
                                                   Location $src, Location $dest) {
        print "Trying to go from " . $src->toString() . " to " .  
              $dest->toString() . "<br/>\n";
        if (IN_A_RUSH) {
            print "<strong>In a rush</strong><br/>\n";
        }
        print "Distance is " . $distance . " in the direction of " .  
              $angle . " degrees<br/>";
    }

}
?>
3.2.3 封装主函数

travel 函数封装到 Travel 类中,并将判断是否有出行选项的逻辑提取到 doWeHaveOptions 方法中:

<?php

error_reporting(E_ALL);
require_once ('config.php');
require_once ('location.php');
require_once ('travelView.php');

class Travel{

    public function execute(Location $src, Location $dest) {

        //calculate the direction vector
        $distance_y = $dest->y - $src->y;
        $distance_x = $dest->x - $src->x;
        $angle = null;
        $time = 0.0;

        if ($distance_x) {
            if ($distance_y) {
                $angle = atan($distance_y / $distance_x);
            } else {
                if ($distance_x > 0) {
                    $angle = 0.0; //right
                } else {
                    $angle = 180.0; //left
                }
            }
        } else {
            if ($distance_y) {
                if ($distance_y < 0) {
                    $angle = - 90.0;    //down
                } else {
                    $angle = 90.0;      //up
                }
            }
        }
        $angle_in_radians = deg2rad ( $angle );

        $distance = 0.0;
        //calculate the straight line distance
        if ($dest->y == $src->y) {
            $distance = $dest->x - $src->x;
        } else if ($dest->x == $src->x) {
            $distance = $dest->y - $src->y;
        } else {
            $distance = sqrt ( ($distance_x * $distance_x) +  
                           ($distance_y * $distance_y) );
        }

        TravelView::displayOurIntendedPath($angle, $distance, $src, $dest);

        $has_options = $this->doWeHaveOptions();

        if ($has_options) {
            if (STORMY_WEATHER) {
                if (HAS_CAR) {
                    //drive
                    while ( abs ( $src->x - $dest->x ) > CAR_STEP ||
                                        abs ( $src->y - $dest->y ) > CAR_STEP ) {
                        $src->x += (CAR_STEP * cos ( $angle_in_radians ));
                        $src->y += (CAR_STEP * sin ( $angle_in_radians ));
                        ++ $time;
                        print "driving a car... currently at (" .  
                                              round ( $src->x, 2 ) . ",  " .  
                                              round ( $src->y, 2 ) . ")<br/>\n";
                    }
                    print "Got to destination by driving a car<br/>";
                } else if (HAS_MONEY && ON_BUS_ROUTE) {
                    //take the bus
                    while ( abs ( $src->x - $dest->x ) > BUS_STEP ||  
                                        abs ( $src->y - $dest->y ) > BUS_STEP ) {
                        $src->x += (BUS_STEP * cos ( $angle_in_radians ));
                        $src->y += (BUS_STEP * sin ( $angle_in_radians ));
                        ++ $time;
                        print "on the bus... currently at (" .  
                                               round ( $src->x, 2 ) . ",  " .  
                                               round ( $src->y, 2 ) . ")<br/>\n";
                    }
                    print "Got to destination by riding the bus<br/>";
                } else {
                    //ride bike
                    while ( abs ( $src->x - $dest->x ) > BIKE_STEP ||  
                                        abs ( $src->y - $dest->y ) > BIKE_STEP ) {
                        $src->x += (BIKE_STEP * cos ( $angle_in_radians ));
                        $src->y += (BIKE_STEP * sin ( $angle_in_radians ));
                        ++ $time;
                        print "biking... currently at (" .  
                                               round ( $src->x, 2 ) . ",  " .  
                                               round ( $src->y, 2 ) . ")<br/>\n";
                    }
                    print "Got to destination by biking<br/>";
                }
            } else {
                if ($distance < WALKING_MAX_DISTANCE && ! IN_A_RUSH) { //walk
                    while ( abs ( $src->x - $dest->x ) > WALK_STEP ||  
                                        abs ( $src->y - $dest->y ) > WALK_STEP ) {
                        $src->x += (WALK_STEP * cos ( $angle_in_radians ));
                        $src->y += (WALK_STEP * sin ( $angle_in_radians ));
                        ++ $time;
                        print "walking... currently at (" .  
                                               round ( $src->x, 2 ) . ",  " .  
                                               round ( $src->y, 2 ) . ")<br/>\n";
                    }
                    print "Got to destination by walking<br/>";
                } else {
                    if (HAS_CAR) {
                        //drive
                        $time += CAR_DELAY;
                        while ( abs ( $src->x - $dest->x ) > CAR_STEP ||  
                                                        abs ( $src->y - $dest->y ) > CAR_STEP ) {
                            $src->x += (CAR_STEP *  
                                                            cos ( $angle_in_radians ));
                            $src->y += (CAR_STEP *  
                                                            sin ( $angle_in_radians ));
                            ++ $time;
                            print "driving a car... currently at (" .     
                                                                   round ( $src->x, 2 ) . ",  " .  
                                                                   round ( $src->y, 2 ) . ")<br/>\n";
                        }
                        print "Got to destination by driving a car<br/>";
                    } else if (HAS_MONEY && ON_BUS_ROUTE) {
                        //take the bus
                        $time += BUS_DELAY;
                        while ( abs ( $src->x - $dest->x ) > BUS_STEP ||  
                                                        abs ( $src->y - $dest->y ) > BUS_STEP ) {
                            $src->x += (BUS_STEP *  
                                                            cos ( $angle_in_radians ));
                            $src->y += (BUS_STEP *  
                                                            sin ( $angle_in_radians ));
                            ++ $time;
                            print "on the bus... currently at (" . 
                                                                  round ( $src->x, 2 ) . ",  " .  
                                                                  round ( $src->y, 2 ) . ")<br/>\n";
                        }
                        print "Got to destination by riding the bus<br/>";
                    } else {
                        //ride bike
                        while ( abs ( $src->x - $dest->x ) > BIKE_STEP ||     
                                                        abs ( $src->y - $dest->y ) > BIKE_STEP ) {
                            $src->x += (BIKE_STEP *  
                                                            cos ( $angle_in_radians ));
                            $src->y += (BIKE_STEP *  
                                                            sin ( $angle_in_radians ));
                            ++ $time;
                            print "biking... currently at (" .  
                                                                   round ( $src->x, 2 ) . ",  " .  
                                                                   round ( $src->y, 2 ) . ")<br/>\n";
                        }
                        print "Got to destination by biking<br/>";
                    }
                }
            }
        } else {
            if (STORMY_WEATHER) {
                print "ERROR: Storming<br/>";
            } else if ($distance < WALKING_MAX_DISTANCE) {
                //walk
                while ( abs ( $src->x - $dest->x ) > WALK_STEP ||  
                                abs ( $src->y - $dest->y ) > WALK_STEP ) {
                    $src->x += (WALK_STEP * cos ( $angle_in_radians ));
                    $src->y += (WALK_STEP * sin ( $angle_in_radians ));
                    ++ $time;
                    print "walking... currently at (" .  
                                          round ( $src->x, 2 ) . ",  " .  
                                          round ( $src->y, 2 ) . ")<br/>\n";
                }
                print "Got to destination by walking<br/>";
            } else {
                print "ERROR: Too far to walk<br/>";
            }
        }

        print "Total time was: " . date ( "i:s", $time );
    }

    private function doWeHaveOptions(){

        $has_options = false;
        if (HAS_CAR || (HAS_MONEY && ON_BUS_ROUTE) || HAS_BIKE) {
            $has_options = true;
        }

        return $has_options;
    }
}
?>

3.3 第二轮重构

3.3.1 提取出行逻辑

将开车、坐公交、步行和骑自行车的代码提取到独立的方法中,使 execute 方法更加简洁。

3.3.2 提取数学计算

将数学计算逻辑提取到 travelMath.php 文件中的 TravelMath 类中:

<?php

error_reporting ( E_ALL );
require_once ('location.php');

class TravelMath {

    public static function calculateDistance(Location $src, Location $dest) {
        $distance = 0.0;
        //calculate the straight line distance
        if ($dest->y == $src->y) {
            $distance = $dest->x - $src->x;
        } else if ($dest->x == $src->x) {
            $distance = $dest->y - $src->y;
        } else {
            $distance_y = $dest->y - $src->y;
            $distance_x = $dest->x - $src->x;
            $distance = sqrt ( ($distance_x * $distance_x) +  
               ($distance_y * $distance_y) );
        }
        return $distance;
    }

    public static function calculateAngleInDegrees(Location $src, Location $dest) {
        //calculate the direction vector
        $distance_y = $dest->y - $src->y;
        $distance_x = $dest->x - $src->x;
        $angle = null;
        if ($distance_x) {
            if ($distance_y) {
                $angle = atan($distance_y / $distance_x);
            } else {
                if ($distance_x > 0) {
                    $angle = 0.0; //right
                } else {
                    $angle = 180.0; //left
                }
            }
        } else {
            if ($distance_y) {
                if ($distance_y < 0) {
                    $angle = - 90.0;    //down
                } else {
                    $angle = 90.0;      //up
                }
            }
        }
        return $angle;
    }

    public static function isCloseToDest(Location $src, Location $dest, $step) {
        return (abs ( $src->x - $dest->x ) < $step ||  
                        abs ( $src->y - $dest->y ) < $step );
    }
}
?>

重构后的 Travel 类如下:

<?php

error_reporting(E_ALL);
require_once('config.php');
require_once('location.php');
require_once('travelMath.php');
require_once('travelView.php');

class Travel
{
    private $src = null;
    private $dest = null;
    private $time = 0.0;

    public function execute(Location $src, Location $dest)
    {
        $this->src = $src;
        $this->dest = $dest;
        $this->time = 0.0;

        $angle = TravelMath::calculateAngleInDegrees($src, $dest);
        $angle_in_radians = deg2rad($angle);
        $distance = TravelMath::calculateDistance($src, $dest);

        TravelView::displayOurIntendedPath($angle, $distance, $src, $dest);
        $has_options = $this->doWeHaveOptions();

        if ($has_options)
        {
            if (STORMY_WEATHER)
            {
                if (HAS_CAR)
                {
                    $this->driveCar($angle_in_radians);
                } else if (HAS_MONEY && ON_BUS_ROUTE)
                {
                    $this->rideBus($angle_in_radians);
                } else
                {
                    $this->rideBike($angle_in_radians);
                }
            } else
            {
                if ($distance < WALKING_MAX_DISTANCE && !IN_A_RUSH)
                {
                    $this->walk($angle_in_radians);
                } else
                {
                    if (HAS_CAR)
                    {
                        $this->driveCar($angle_in_radians);
                    } else if (HAS_MONEY && ON_BUS_ROUTE)
                    {
                        $this->rideBus($angle_in_radians);
                    } else
                    {
                        $this->rideBike($angle_in_radians);
                    }
                }
            }
        } else
        {
            if (STORMY_WEATHER)
            {
                print "ERROR: Storming<br/>";
            } else if ($distance < WALKING_MAX_DISTANCE)
            {
                $this->walk($angle_in_radians);
            } else
            {
                print "ERROR: Too far to walk<br/>";
            }
        }
        print "Total time was: " . date("i:s", $this->time);
    }

    private function doWeHaveOptions()
    {
        $has_options = false;
        if (HAS_CAR || (HAS_MONEY && ON_BUS_ROUTE) || HAS_BIKE)
        {
            $has_options = true;
        }
        return $has_options;
    }

    private function driveCar($angle_in_radians)
    {
        $this->time += CAR_DELAY;
        //drive
        while (abs($this->src->x - $this->dest->x) > CAR_STEP ||
        abs($this->src->y - $this->dest->y) > CAR_STEP)
        {
            $this->src->x += ( CAR_STEP * cos($angle_in_radians));
            $this->src->y += ( CAR_STEP * sin($angle_in_radians));
            ++$this->time;
            print "driving a car... currently at (" . round($this->src->x, 2) .
                    ", " . round($this->src->y, 2) . ")<br/>\n";
        }

        print "Got to destination by driving a car<br/>";
    }

    private function rideBus($angle_in_radians)
    {
        //take the bus
        $this->time += BUS_DELAY;
        while (abs($this->src->x - $this->dest->x) > BUS_STEP ||
        abs($this->src->y - $this->dest->y) > BUS_STEP)
        {
            $this->src->x += ( BUS_STEP * cos($angle_in_radians));
            $this->src->y += ( BUS_STEP * sin($angle_in_radians));
            ++$this->time;
            print "on the bus... currently at (" . round($this->src->x, 2) .
                    ", " . round($this->src->y, 2) . ")<br/>\n";
        }
        print "Got to destination by riding the bus<br/>";
    }

    private function rideBike($angle_in_radians)
    {
        //ride bike
        while (abs($this->src->x - $this->dest->x) > BIKE_STEP ||
        abs($this->src->y - $this->dest->y) > BIKE_STEP)
        {
            $this->src->x += ( BIKE_STEP * cos($angle_in_radians));
            $this->src->y += ( BIKE_STEP * sin($angle_in_radians));
            ++$this->time;
            print "biking... currently at (" . round($this->src->x, 2) .
                    ", " . round($this->src->y, 2) . ")<br/>\n";
        }
        print "Got to destination by biking<br/>";
    }

    private function walk($angle_in_radians)
    {
        while (abs($this->src->x - $this->dest->x) > WALK_STEP ||
        abs($this->src->y - $this->dest->y) > WALK_STEP)
        {
            $this->src->x += ( WALK_STEP * cos($angle_in_radians));
            $this->src->y += ( WALK_STEP * sin($angle_in_radians));
            ++$this->time;
            print "walking... currently at (" . round($this->src->x, 2) .
                    ", " . round($this->src->y, 2) . ")<br/>\n";
        }
        print "Got to destination by walking<br/>";
    }
}
?>

3.4 重构总结

通过两轮重构,代码的结构得到了显著改善,嵌套层级减少,职责更加明确,重复代码也得到了有效消除。以下是重构前后的对比:
| 对比项 | 重构前 | 重构后 |
| ---- | ---- | ---- |
| 代码结构 | 单一大型函数,嵌套层级多 | 多个类和方法,结构清晰 |
| 可维护性 | 差,难以添加功能和测试 | 好,易于扩展和维护 |
| 可读性 | 低,代码复杂 | 高,逻辑清晰 |

在重构过程中,需要不断问自己一些基本问题,如哪些部分容易修改、是否有重复代码、能否简化代码以及是否可以添加测试等。同时,添加测试也是重构过程中的重要环节,能帮助及时发现和修复引入的问题。

总之,对于大型遗留代码,通过合理的重构和测试,可以使其变得更加健壮、可维护和易于扩展。开发者在实际工作中,应根据代码的具体情况,灵活运用重构技巧,不断优化代码质量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值