重构:多态类型的重构

一.重构前

1.实用类

public class Item {
    public static final String SULFURAS = "Sulfuras";
    public static final String AGED_BRIE = "Aged Brie";
    public static final String BACKSTAGE_PASSES = "Backstage passes";
    public static final String NORMAL = "Normal";
    private final int sellIn;
    private final int quality;
    private final String name;

    public static Item newInstanceWithNameSellInAndQuality(String name, int sellIn, int quality) {
        if (!name.equals(SULFURAS) && (quality > 50 || quality < 0)) {
            throw new IllegalArgumentException("The quality of the normal item should be between 0 and 50.");
        }
        return new Item(name, sellIn, quality);
    }

    private Item(String name, int sellIn, int quality) {
        this.name = name;
        this.sellIn = sellIn;
        this.quality = quality;
    }

    public int getSellIn() {
        return sellIn;
    }

    public Item updateSellInAndQuality() {
        if (name.equals(SULFURAS)) {
            return new Item(name, sellIn - 1, quality);
        }
        if (name.equals(AGED_BRIE)) {
            return new Item(name, sellIn - 1, notGreaterThanFifty(quality + 1));
        }
        if (name.equals(BACKSTAGE_PASSES)) {
            return new Item(name, sellIn - 1, notGreaterThanFifty(sellIn > 10 ? quality + 1 : (sellIn > 5 ? quality + 2 : (sellIn > 0 ? quality + 3 : 0))));
        }
        return new Item(name, sellIn - 1, notLessThanZero((sellIn <= 0) ? quality - 2 : quality - 1));
    }

    private int notGreaterThanFifty(int quality) {
        return quality > 50 ? 50 : quality;
    }

    private int notLessThanZero(int quality) {
        return quality <= 0 ? 0 : quality;
    }

    public int getQuality() {
        return quality;
    }
}

2.测试类

注意:重构的前提条件是正确的测试类

package org.katas;

import org.junit.Test;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;

public class GildedRoseTest {
    @Test
    public void at_the_end_of_each_day_our_system_lowers_sellIn_and_quality_values_by_1_if_sellIn_is_greater_than_0_for_normal_items() {
        Item item = Item.newInstanceWithNameSellInAndQuality(Item.NORMAL, 1, 20);

        Item updatedItem = item.updateSellInAndQuality();

        assertThat(updatedItem.getSellIn(), is(0));
        assertThat(updatedItem.getQuality(), is(19));
    }

    @Test
    public void once_the_sell_by_date_has_passed_quality_of_normal_items_degrades_twice_as_fast() {
        Item item = Item.newInstanceWithNameSellInAndQuality(Item.NORMAL, 0, 20);

        Item updatedItem = item.updateSellInAndQuality();

        assertThat(updatedItem.getSellIn(), is(-1));
        assertThat(updatedItem.getQuality(), is(18));
    }

    @Test
    public void the_quality_of_normal_items_should_be_greater_than_or_equal_to_0() {
        Item item = Item.newInstanceWithNameSellInAndQuality(Item.NORMAL, 0, 0);

        Item updatedItem = item.updateSellInAndQuality();

        assertThat(updatedItem.getSellIn(), is(-1));
        assertThat(updatedItem.getQuality(), is(0));
    }

    @Test(expected = IllegalArgumentException.class)
    public void the_quality_of_normal_items_should_be_no_more_than_50() {
        Item item = Item.newInstanceWithNameSellInAndQuality(Item.NORMAL, 0, 51);
    }

    @Test
    public void the_Aged_Brie_actually_increases_in_Quality_by_1_the_older_it_gets() {
        Item item = Item.newInstanceWithNameSellInAndQuality(Item.AGED_BRIE, 0, 0);

        Item updatedItem = item.updateSellInAndQuality();

        assertThat(updatedItem.getSellIn(), is(-1));
        assertThat(updatedItem.getQuality(), is(1));
    }

    @Test
    public void the_quality_of_Aged_Brie_should_not_exceed_50() {
        Item item = Item.newInstanceWithNameSellInAndQuality(Item.AGED_BRIE, -1, 50);

        Item updatedItem = item.updateSellInAndQuality();

        assertThat(updatedItem.getSellIn(), is(-2));
        assertThat(updatedItem.getQuality(), is(50));
    }

    @Test
    public void the_Sulfuras_is_a_legendary_item_and_as_such_its_Quality_is_80_and_it_never_alters() {
        Item item = Item.newInstanceWithNameSellInAndQuality(Item.SULFURAS, -1, 80);

        Item updatedItem = item.updateSellInAndQuality();

        assertThat(updatedItem.getSellIn(), is(-2));
        assertThat(updatedItem.getQuality(), is(80));
    }

    @Test
    public void the_quality_of_backstage_passes_increases_by_1_when_the_sellIn_are_more_than_10_days() {
        Item item = Item.newInstanceWithNameSellInAndQuality(Item.BACKSTAGE_PASSES, 11, 20);

        Item updatedItem = item.updateSellInAndQuality();

        assertThat(updatedItem.getSellIn(), is(10));
        assertThat(updatedItem.getQuality(), is(21));
    }

    @Test
    public void the_quality_of_backstage_passes_increases_by_2_when_the_sellIn_are_between_5_and_10_days() {
        Item item = Item.newInstanceWithNameSellInAndQuality(Item.BACKSTAGE_PASSES, 10, 20);

        Item updatedItem = item.updateSellInAndQuality();

        assertThat(updatedItem.getSellIn(), is(9));
        assertThat(updatedItem.getQuality(), is(22));
    }

    @Test
    public void the_quality_of_backstage_passes_increases_by_3_when_the_sellIn_are_between_0_and_5_days() {
        Item item = Item.newInstanceWithNameSellInAndQuality(Item.BACKSTAGE_PASSES, 5, 20);

        Item updatedItem = item.updateSellInAndQuality();

        assertThat(updatedItem.getSellIn(), is(4));
        assertThat(updatedItem.getQuality(), is(23));
    }

    @Test
    public void the_quality_of_backstage_passes_drops_to_0_after_the_concert() {
        Item item = Item.newInstanceWithNameSellInAndQuality(Item.BACKSTAGE_PASSES, 0, 20);

        Item updatedItem = item.updateSellInAndQuality();

        assertThat(updatedItem.getSellIn(), is(-1));
        assertThat(updatedItem.getQuality(), is(0));
    }

    @Test
    public void the_quality_of_backstage_passes_is_not_greater_than_50() {
        Item item = Item.newInstanceWithNameSellInAndQuality(Item.BACKSTAGE_PASSES, 1, 50);

        Item updatedItem = item.updateSellInAndQuality();

        assertThat(updatedItem.getSellIn(), is(0));
        assertThat(updatedItem.getQuality(), is(50));
    }
}

二.重构之后

1.实体类

package org.katas;

public class Item {
    public static final String SULFURAS = "Sulfuras";
    public static final String AGED_BRIE = "Aged Brie";
    public static final String BACKSTAGE_PASSES = "Backstage passes";
    public static final String NORMAL = "Normal";
    private final int sellIn;
    private final int quality;
    private static Sell sell;

    public static Item newInstanceWithNameSellInAndQuality(String name, int sellIn, int quality) {
        setSell(name);
        return sell.newInstanceWithNameSellInAndQuality(sellIn, quality);
    }

    public Item(String name, int sellIn, int quality) {
        this.sellIn = sellIn;
        this.quality = quality;
    }

    public int getSellIn() {
        return sellIn;
    }

    public static void setSell(String name) {
        if(name.equals(SULFURAS)) {
            sell = new SulfurasSell();
            return;
        }
        if(name.equals(AGED_BRIE)) {
            sell = new AgedBrieSell();
            return;
        }
        if(name.equals(BACKSTAGE_PASSES)) {
            sell = new BackstagePassesSell();
            return;
        }
        sell = new NormalSell();
    }
    
    public Item updateSellInAndQuality() {
        return sell.updateSellInAndQuality(sellIn, quality);
    }

    public int getQuality() {
        return quality;
    }
}

2.多态类的接口

public interface Sell {
    Item newInstanceWithNameSellInAndQuality(int sellIn, int quality);
    
    Item updateSellInAndQuality(int sellIn, int quality);
}

3.多态类的实现

package org.katas;

public class AgedBrieSell implements Sell {

    public Item newInstanceWithNameSellInAndQuality(int sellIn, int quality) {
        if (quality > 50 || quality < 0) {
            throw new IllegalArgumentException("The quality of the normal item should be between 0 and 50.");
        }
        return new Item("Aged Brie", sellIn, quality);
    }

    public Item updateSellInAndQuality(int sellIn, int quality) {
        return new Item("Aged Brie", sellIn - 1, notGreaterThanFifty(quality + 1));
    }
    
    private int notGreaterThanFifty(int quality) {
        return quality > 50 ? 50 : quality;
    }
}

package org.katas;

public class BackstagePassesSell implements Sell {
    public Item newInstanceWithNameSellInAndQuality(int sellIn, int quality) {
        if (quality > 50 || quality < 0) {
            throw new IllegalArgumentException("The quality of the normal item should be between 0 and 50.");
        }
        return new Item("Backstage passes", sellIn, quality);
    }

    public Item updateSellInAndQuality(int sellIn, int quality) {
        return new Item("Backstage passes", sellIn - 1, notGreaterThanFifty(sellIn > 10 ? quality + 1 : (sellIn > 5 ? quality + 2 : (sellIn > 0 ? quality + 3 : 0))));
    }
    
    private int notGreaterThanFifty(int quality) {
        return quality > 50 ? 50 : quality;
    }
}

package org.katas;

public class NormalSell implements Sell{

    public Item newInstanceWithNameSellInAndQuality(int sellIn, int quality) {
        if (quality > 50 || quality < 0) {
            throw new IllegalArgumentException("The quality of the normal item should be between 0 and 50.");
        }
        return new Item("Normal", sellIn, quality);
    }

    public Item updateSellInAndQuality(int sellIn, int quality) {
        return new Item("Normal", sellIn - 1, notLessThanZero((sellIn <= 0) ? quality - 2 : quality - 1));
    }

    private int notLessThanZero(int quality) {
        return quality <= 0 ? 0 : quality;
    }
}

package org.katas;

public class SulfurasSell implements Sell {

    public Item newInstanceWithNameSellInAndQuality(int sellIn, int quality) {
        return new Item("Sulfuras", sellIn, quality);
    }

    public Item updateSellInAndQuality(int sellIn, int quality) {
        return new Item("Sulfuras", sellIn - 1, quality);
    }
}

重构完成。

你可能感兴趣的:(重构)