Extreme Programming in Perl Robert Nagler phần 9

pdf
Số trang Extreme Programming in Perl Robert Nagler phần 9 22 Cỡ tệp Extreme Programming in Perl Robert Nagler phần 9 281 KB Lượt tải Extreme Programming in Perl Robert Nagler phần 9 0 Lượt đọc Extreme Programming in Perl Robert Nagler phần 9 0
Đánh giá Extreme Programming in Perl Robert Nagler phần 9
4.2 ( 5 lượt)
Nhấn vào bên dưới để tải tài liệu
Đang xem trước 10 trên tổng 22 trang, để tải xuống xem đầy đủ hãy nhấn vào bên trên
Chủ đề liên quan

Nội dung

14.8 Stylin’ Coding style is important to consider when refactoring. In this chapter, I chose to maintain the style of the source.a To contrast, here’s what access would look like in bOP style: sub a ccess my(f ield,self, value) = @; return @> = 3?self->f ield =value : self − > a There are some minor changes to whitespace, indentation, and brace placement for consistency and brevity within this book. b Except in FORTRAN, where the name of the variable can determine its type. 14.9 Tactics Versus Strategy When we begin a task, tactics are important. We want to add business value as quickly as possible. Doing the simplest thing that could possibly work is a tactic to reach our goal. Copy-and-paste is the weapon of choice when task completion is our only objective. Generalizations, such as the refactorings shown thus far, are not easy to come up with when you’re under pressure to get the job done. Besides, you don’t know if the code you copy solves the problem until the implementation is finished. It’s much better to copy-and-paste to test an idea than to invent, to implement, and to use the wrong abstraction. As a design strategy, copy-and-paste poses problems. The code is more difficult to comprehend, because it’s difficult to see subtle differences. Faults fixed in one place do not propagate themselves automatically. For example, there’s an alternative fix to the problem already embedded in two other accessors, Count and Size: sub Count { my $me = shift; my $c = shift; if (defined $c and length($c) > 0) { $me->{COUNT} = $c; } else { return $me->{COUNT}; } } Copyright c 2004 Robert Nagler All rights reserved nagler@extremeperl.org 134 sub Size { my $me = shift; my $c = shift; if (defined $c and length($c) > 0) { $me->{SIZE} = $c; } else { return $me->{SIZE}; } } These accessors behave differently from the other eight that we refactored and fixed above. Count and Size need to be resettable to zero (mailboxes can be empty), which is why the accessors have an alternate implementation. The thought and debugging that went into fixing Count and Size could have also applied to the other accessors. Since the code wasn’t refactored at the time of the fix, it was probably easier to leave the other accessors alone. And, under the principle of “if it ain’t broke, don’t fix it” any change like this is not justified. XP legitimatizes fixing non-broke code. It’s something programmers do anyway, so XP gives us some structure and guidelines to do it safely. We can refactor Size and Count to use access without fear.4 The unit test covers the empty mailbox case. If we didn’t have a test for this case, we could add one. Again, XP saves us. Since the programmers are the testers, we’re free to modify the test to suit our needs. 14.10 Refactor With a Partner Pair programming supports refactoring, too. Two people are better than one at assessing the need for, the side-effects of, and the difficulty of a change. The tradeoffs between tactics versus strategy are hard, and discussing them with a partner is both effective and natural. Switching partners often brings new perspectives to old code, too. Sometimes I look at code and don’t know where to begin refactoring. The complexity overwhelms my ability to identify commonality. For example, here’s some code which needs refactoring: 4 When making changes to CPAN modules, XP, nor any other methodology, helps to validate uses in the (unknown) importers. Copyright c 2004 Robert Nagler All rights reserved nagler@extremeperl.org 135 sub List { my $me = shift; my $num = shift || ’’; my $CMD = shift || ’LIST’; $CMD=~ tr/a-z/A-Z/; $me->Alive() or return; my @retarray = (); my $ret = ’’; $me->_checkstate(’TRANSACTION’, $CMD) or return; $me->_sockprint($CMD, $num ? " $num" : ’’, $me->EOL()); my $line = $me->_sockread(); unless (defined $line) { $me->Message("Socket read failed for LIST"); return; } $line =~ /^\+OK/ or $me->Message("$line") and return; if ($num) { $line =~ s/^\+OK\s*//; return $line; } while (defined($line = $me->_sockread())) { $line =~ /^\.\s*$/ and last; $ret .= $line; chomp $line; push(@retarray, $line); } if ($ret) { return wantarray ? @retarray : $ret; } } sub ListArray { my $me = shift; my $num = shift || ’’; my $CMD = shift || ’LIST’; $CMD=~ tr/a-z/A-Z/; $me->Alive() or return; my @retarray = (); my $ret = ’’; $me->_checkstate(’TRANSACTION’, $CMD) or return; Copyright c 2004 Robert Nagler All rights reserved nagler@extremeperl.org 136 $me->_sockprint($CMD, $num ? " $num" : ’’, $me->EOL()); my $line = $me->_sockread(); unless (defined $line) { $me->Message("Socket read failed for LIST"); return; } $line =~ /^\+OK/ or $me->Message("$line") and return; if ($num) { $line =~ s/^\+OK\s*//; return $line; } while (defined($line = $me->_sockread())) { $line =~ /^\.\s*$/ and last; $ret .= $line; chomp $line; my($num, $uidl) = split ’ ’, $line; $retarray[$num] = $uidl; } if ($ret) { return wantarray ? @retarray : $ret; } } sub Uidl { my $me = shift; my $num = shift || ’’; $me->Alive() or return; my @retarray = (); my $ret = ’’; $me->_checkstate(’TRANSACTION’, ’UIDL’) or return; $me->_sockprint(’UIDL’, $num ? " $num" : ’’, $me->EOL()); my $line = $me->_sockread(); unless (defined $line) { $me->Message("Socket read failed for UIDL"); return; } $line =~ /^\+OK/ or $me->Message($line) and return; if ($num) { $line =~ s/^\+OK\s*//; return $line; Copyright c 2004 Robert Nagler All rights reserved nagler@extremeperl.org 137 } while (defined($line = $me->_sockread())) { $line =~ /^\.\s*$/ and last; $ret .= $line; chomp $line; my($num, $uidl) = split ’ ’, $line; $retarray[$num] = $uidl; } if ($ret) { return wantarray ? @retarray : $ret; } } Where are the differences? What’s the first step? With a fresh perspective, the following stood out: sub Uidl { my $me = shift; my $num = shift; return $me->ListArray($num, ’UIDL’); } A partner helps you overcome familiarity and fear of change which make it hard to see simplifications like this one. 14.11 Sharing with Code References It’s clear that List and ListArray are almost identical. The problem is that they differ in the middle of the loop. Perl code references are a great way to factor out differences especially within loops: sub List { return list( sub { my $line = shift; my shift; push(@$retarray, $line); return; }, @ , ); } { return list( sub { my($num, $value) = split ’ ’, = shift; $retarray->[$num] = $value; return; }, @ , { my $parse line = shift; my $me = shift; Copyright c 2004 Robert Nagler All rights reserved nagler@extremeperl.org $retarray = sub ListArray shift; my $retarray ); } sub list 138 my $num = shift || ’’; my $CMD = shift || ’LIST’; $CMD =~ tr/a-z/A-Z/; $me->Alive() or return; my @retarray = (); my $ret = ’’; $me->_checkstate(’TRANSACTION’, $CMD) or return; $me->_sockprint($CMD, $num ? " $num" : ’’, $me->EOL()); my $line = $me->_sockread(); unless (defined $line) { $me->Message("Socket read failed for $CMD"); return; } $line =~ /^\+OK/ or $me->Message("$line") and return; if ($num) { $line =~ s/^\+OK\s*//; return $line; } while (defined($line = $me->_sockread())) { $line =~ /^\.\s*$/ and last; $ret .= $line; chomp $line; $parse line->($line, \@retarray); } if ($ret) { return wantarray ? @retarray : $ret; } } We pass an anonymous subroutine as list’s first parameter, $parse line, for the reason described in Refactor Then Fix. 14.12 Refactoring Illuminates Fixes The List, ListArray, and Uidl methods are bimodal. When called without arguments, they return a list of values. When passed a message number, the value for that message number alone is returned. The message number cases failed in our unit test. The code reference refactoring shows us where the fault lies: $parse line Copyright c 2004 Robert Nagler All rights reserved nagler@extremeperl.org 139 is not called when list is called with an argument. It also needs to chomp the $line to match the behavior: if ($num) { $line =~ s/^\+OK\s*//; chomp $line; return $parse line->($line); } The anonymous subroutines in List and ListArray need to be bimodal for this refactoring to work: sub List { return _list( sub { my $line = shift; my $retarray = shift or return $line; push(@$retarray, $line); return; }, @_, ); } sub ListArray { return _list( sub { my($num, $value) = split ’ ’, shift; my $retarray = shift or return $value; $retarray->[$num] = $value; return; }, @_, ); } By compressing the business logic, its essence and errors become apparent. Less code is almost always better than more code. While advanced constructs like code references may be difficult to understand for those unfamiliar with them, dumbing down the code is not a Copyright c 2004 Robert Nagler All rights reserved nagler@extremeperl.org 140 good option. Defaulting to the least common denominator produces dumb code and ignorant programmers. In XP, change occurs not only the project artifacts but also in ourselves. Learning and teaching are an integral part of the XP methodology. 14.13 Brush and Floss Regularly This chapter presents a glimpse of design on demand. Each refactoring was implemented and tested separately. The trick to refactoring successfully is taking baby steps. I like to compare refactoring to brushing your teeth. Your best shot at preventing tooth decay is to brush briefly after each meal and to floss daily. Alternatively, you could just wait for cavities to appear and have them filled. The short term cost in pain, money, and time is much greater if you do. In the long term, too many fillings create structural problems and the tooth has to be replaced. Refactoring is preventative maintenance, like tooth brushing. Quick fixes are like fillings. Eventually, they create structural problems and require the implementation to be replaced. Like teeth, complete rewrites are unnecessarily painful and costly. XP encourages you to do the simplest thing that could possibly work (tactics) to address the immediate problem . You refactor your code to express concepts once and only once (strategy) to prevent structural problems. And, don’t forget that brushing and flossing support pair programming. Copyright c 2004 Robert Nagler All rights reserved nagler@extremeperl.org 141 Copyright c 2004 Robert Nagler All rights reserved nagler@extremeperl.org 142 Chapter 15 It’s a SMOP Representation is the essence of programming. – Fred Brooks Implementing Extreme Perl is a simple matter of programming. Practicing XP clarifies its values. Perl’s virtues come alive as you read and write it. The subject matter language crystallizes as the subject matter oriented program evolves along with the programmers’ mastery of the problem domain. This chapter coalesces the book’s themes (XP, Perl, and SMOP) into a single example: a DocBook XML to HTML translator. We walk through the planning game, split the story into tasks, discuss coding style, design simply, pair program, test first, and iterate. The subject matter oriented program (SMOP) is a hash of XML to HTML tags interpreted by a declarative algorithm. Along the way, declarative programming is defined and related to other paradigms (object-oriented and imperative programming). 15.1 The Problem The example in this chapter converts DocBook XML1 (the source form of this book) to HTML. The example went beyond this chapter, and the version here was enhanced to produce the review copies for the entire book.2 DocBook is a technical document description language. I described the paragraphs, sections, terms, etc. of this book with tags as follows: 1 DocBook: The Definitive Guide by Norman Walsh and Leonard Muellner, available online at http://www.docbook.org/tdg/en/html/docbook.html 2 The most recent version is available with bOP. 143
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.