Legacy codesmalltalk

22
Legacy Code Quit Complaining, Start Fixing Saturday, February 15, 14

Transcript of Legacy codesmalltalk

Page 1: Legacy codesmalltalk

Legacy CodeQuit Complaining, Start Fixing

Saturday, February 15, 14

Page 2: Legacy codesmalltalk

What Is Legacy Code?

Saturday, February 15, 14

Page 3: Legacy codesmalltalk

What Is Legacy Code?

1.     /**2.          *3.          * @param balanceData4.          */5.         public void populateCashAndInvestmentsForCurrency(List<BalanceData> balanceData, String currency) {6.  7.                 PieChartItem item = new PieChartItem();8.                 Resources res = getResources();9.  10.                 for (BalanceData data : balanceData) {11.  12.                         // if associated currency then do13.                         if (data.getCashBalanceCurrency().equals(currency)) {14.                                 BigDecimal cash = BigDecimal.ZERO;15.                                 BigDecimal investment = BigDecimal.ZERO;16.                                 List<PieChartItem> slices = new ArrayList<PieChartItem>();17.  18.                                 if (data.getIsOffshore()) { // offshore19.  20.                                         cash = data.getCashBalance();21.  22.                                         investment = data.getInvestmentBalance();23.  24.                                         for (ExchangeRate rate : <hidden>.getExchangeRates()) {25.  26.                                                 if (rate.getCurrency().equals(data.getCashBalanceCurrency())) {27.  28.                                                         cash = cash.multiply(rate.getRate());29.  30.                                                         investment = investment.multiply(rate.getRate());31.  32.                                                 }33.  34.                                         }35.  36.                                 } else if (!data.getIsOffshore()) { // local37.  38.                                         cash = data.getCashBalance();39.  40.                                         investment = data.getInvestmentBalance();41.  42.                                 }43.  44.                                 if (<hidden>.getmCurrentExchangeRate().getRate().compareTo(newBigDecimal("0.0000")) == 1) {45.                                         // convert to current rate46.                                         cash = cash.divide(<hidden>.getmCurrentExchangeRate().getRate(),BigDecimal.ROUND_UP);47.                                         investment =investment.divide(<hidden>.getmCurrentExchangeRate().getRate(),

BigDecimal.ROUND_UP);48.                                 }49.  50.                                 // investment slice51.                                 item = new PieChartItem();52.                                 item.mLabel = res.getString(R.string.pcs_investment);53.                                 item.mSliceValue = investment;54.                                 item.mValue = investment.floatValue();55.                                 item.mColor = res.getColor(R.color.pcs_teal);56.                                 slices.add(item);57.  58.                                 // cash slice59.                                 item = new PieChartItem();60.                                 item.mLabel = res.getString(R.string.pcs_cash);61.                                 item.mSliceValue = cash;62.                                 item.mValue = cash.floatValue();63.                                 item.mColor = res.getColor(R.color.pcs_biscuit);64.                                 slices.add(item);65.  66.                                 mPieChartList.add(slices);67.                         }68.  69.                 }70.  71.         }

Saturday, February 15, 14

Page 4: Legacy codesmalltalk

What Is Legacy Code?

Code where the tests are an impediment, not a benefit

Saturday, February 15, 14

Page 5: Legacy codesmalltalk

Vent

But get it out of your system

Get it in a Harness

Verify the code around your new code

Add your Code

Steps

Saturday, February 15, 14

Page 6: Legacy codesmalltalk

Seams

A place where you can change existing behavior without editing code

Objects, Linker, Preprocessor

Enabling Point

Saturday, February 15, 14

Page 7: Legacy codesmalltalk

var ParseRecurrenceDate = function (recurrencedata) { var StartTime = DTSTART.exec(recurrencedata)[1]; var EndTime = DTEND.exec(recurrencedata)[1]; if (StartTime.indexOf("T") > 0) { ... } var EndDate = RRULE.exec(recurrencedata)[1]; EndDate = new Date(parseInt(EndDate.substr(0, 4), 10), parseInt(EndDate.substr(4, 2), 10) - 1, parseInt(EndDate.substr(6, 2), 10));

Saturday, February 15, 14

Page 8: Legacy codesmalltalk

      (will-mount [_]        (let [{:keys [id-key filter tag-fn]} opts              kill-chan (om/get-state owner :kill-chan)              tx-chan (om/get-shared owner :tx-chan)              txs (chan)]          (assert (not (nil? tx-chan))            "om-sync requires shared :tx-chan pub channel with :txs topic")          (async/sub tx-chan :txs txs)          (om/set-state! owner :txs txs)          (go (loop []                (let [dpath (om/path coll)                      [{:keys [path new-value new-state] :as tx-data} _] (<! txs)                      ppath (popn (- (count path) (inc (count dpath))) path)]                  (when (and (subpath? dpath path)                             (or (nil? filter) (filter tx-data)))                    (let [tag (if-not (nil? tag-fn)

Saturday, February 15, 14

Page 9: Legacy codesmalltalk

   private void paintBackgroundDisabledAndWindowMaximized(Graphics2D g) {166         roundRect = decodeRoundRect1();167         g.setPaint(decodeGradient1(roundRect));168         g.fill(roundRect);169         roundRect = decodeRoundRect2();170         g.setPaint(decodeGradient2(roundRect));171         g.fill(roundRect);172         rect = decodeRect1();173         g.setPaint(color4);174         g.fill(rect);175         rect = decodeRect2();176         g.setPaint(color5);177         g.fill(rect);178         rect = decodeRect3();

Saturday, February 15, 14

Page 10: Legacy codesmalltalk

Adapt Parameter

Create New Interface That you can fake

Wrap Hard to instantiate class

Move to interfaces that communicate responsibilities not implementation - to avoid over-mocking

Saturday, February 15, 14

Page 11: Legacy codesmalltalk

   private void paintBackgroundDisabledAndWindowMaximized(GraphicsAdapter g) {166         roundRect = decodeRoundRect1();167         g.setPaint(decodeGradient1(roundRect));168         g.fill(roundRect);169         roundRect = decodeRoundRect2();170         g.setPaint(decodeGradient2(roundRect));171         g.fill(roundRect);172         rect = decodeRect1();173         g.setPaint(color4);174         g.fill(rect);175         rect = decodeRect2();176         g.setPaint(color5);177         g.fill(rect);178         rect = decodeRect3();

Saturday, February 15, 14

Page 12: Legacy codesmalltalk

public class StubGraphicsAdapter implements GraphicsAdapter {  public void setPaint(java.awt.Color color) { } ...} public class JavaAwtGraphicsAdapter implements GraphicsAdapter { private Graphics2D g; public JavaAwtGraphicsAdapter(Graphics2D g) { this.g = g; } public void setPaint(java.awt.Color color) { this.g.setPaint(color); }

Saturday, February 15, 14

Page 13: Legacy codesmalltalk

UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Signup Unsuccessful" message:reason delegate:NULL cancelButtonTitle:@"OK" otherButtonTitles:NULL];

Saturday, February 15, 14

Page 14: Legacy codesmalltalk

Extract and Override

Identify the call you want to extract

Create a new method on the current class

Override in test

Saturday, February 15, 14

Page 15: Legacy codesmalltalk

UIAlertView *alert =[[[[self class] alertViewClass] alloc] initWithTitle:@"Signup message:reason delegate:NULL cancelButtonTitle:@"OK" otherButtonTitles:NULL];

Saturday, February 15, 14

Page 16: Legacy codesmalltalk

+(Class) alertViewClass{ return [UIAlertView class];} // In test file@interface LoginViewControllerWithTestAlert : LoginViewController@end @implementation LoginViewControllerWithTestAlert +(Class) alertViewClass{ return [TestAlert class];} @end

Saturday, February 15, 14

Page 17: Legacy codesmalltalk

Static Setter

Allow Subclassing a Singleton

Add a Static Setter

Add a Clear or Reset to avoid Test Pollution

Saturday, February 15, 14

Page 18: Legacy codesmalltalk

void MessageRouter::route(Message *message) { Dispatcher *dispatcher = ExternalRouter::instance()->getDispatcher(); if (dispatcher != NULL) dispatcher->sendMessage(message;}

Saturday, February 15, 14

Page 19: Legacy codesmalltalk

class ExternalRouter{ private: static ExternalRouter *_instance; ExternalRouter(); public: static ExternalRouter *instance();} ExternalRouter *ExternalRouter::_instance = 0; ExternalRouter *ExternalRouter::instance(){ if (_instance == 0) { _instance = new ExternalRouter(); } return _instance;}

Saturday, February 15, 14

Page 20: Legacy codesmalltalk

class ExternalRouter{ private: static ExternalRouter *_instance; protected: ExternalRouter(); public: static ExternalRouter *instance(); static void setTestingInstance(ExternalRouter *newInstance);} void ExternalRouter::setTestingInstance(ExternalRouter *newInstance){ delete _instance; _instance = newInstance;} class TestingExternalRouter : public ExternalRouter{ public: virtual void Dispatcher *getDispatcher() const { return new FakeDispatcher(); }}

Saturday, February 15, 14

Page 21: Legacy codesmalltalk

Higher Level Tests

Characterization Tests

In case of emergency break glass.

Saturday, February 15, 14

Page 22: Legacy codesmalltalk

Resources

Working Effectively With Legacy Code - Michael Feathers

http://www.reddit.com/r/badcode

@paytonrules (that’s me)

Bullets suck

Saturday, February 15, 14