软件设计重构秘笈1式-00封装集合

138 阅读3分钟

软件设计重构秘笈1式-00封装集合

概念

本文所讲的封装集合就是把集合进行封装,只提供调用端需要的接口。

意图

在很多时候,我们都不希望把一些不必要的操作暴露给调用端,只需要给它所需要的操作或数据就行,那么做法就是封装。 本例子介绍了集合的封装方法,当某些类需要向类的使用者提供类中的集合时,而且该集合的addXXXXX方法是经过了一些特别处理的时候,如果这时类的使用者通过get方法获得集合的时候,再使用集合本身的add方法的时候,就会绕过类的addXXXXX方法,就不能达到预期的目的。 本节将介绍一种方法:既能让类的使用者使用集合,又能防止使用者向集合中添加数据、删除数据等修改操作。

案例

【订单案例】

/**
 * 订单管理类
 */
public class Order {
    private double orderTotal;

    private List<OrderItem> orderItems = new ArrayList<>();

    /**
     * 获取订单列表
     *
     * @return 订单列表
     */
    public List<OrderItem> getOrderItems() {
        return orderItems;
    }

    /**
     * 添加订单
     *
     * @param orderItem 订单
     */
    public void addOrderItem(OrderItem orderItem) {
        if (orderItem == null) {
            return;
        }
        orderItems.add(orderItem);
        orderTotal += orderItem.getPrice();
    }

    /**
     * 删除订单
     *
     * @param orderItem 订单
     */
    public void removeOrderItem(OrderItem orderItem) {
        if (orderItem == null) {
            return;
        }
        if (orderItems.contains(orderItem)) {
            orderItems.remove(orderItem);
            orderTotal -= orderItem.getPrice();
        }
    }

    /**
     * 获取订单总额
     *
     * @return 订单总额
     */
    public double getOrderTotal() {
        return orderTotal;
    }
}


/**
 * 订单
 */
public class OrderItem {
    private double price;

    public OrderItem(double price) {
        this.price = price;
    }

    /**
     * 获取订单价格
     *
     * @return 价格
     */
    public double getPrice() {
        return price;
    }
}

public class Test {
    public static void main(String[] args) {
        Order order = new Order();
        order.addOrderItem(new OrderItem(50));
        order.addOrderItem(new OrderItem(100));
        List<OrderItem> orderItems = order.getOrderItems();
        // 调用了list自身的方法,而不是Order提供的添加订单方法,导致了最后总额出错
        orderItems.add(new OrderItem(40));

        int index = 1;
        for (OrderItem orderItem : orderItems) {
            System.out.println("订单" + index + "价格: " + orderItem.getPrice());
        }
        System.out.println("订单总额:" + order.getOrderTotal());
    }
}

测试结果:

1 订单1价格: 50.0
2 订单1价格: 100.0
3 订单1价格: 40.0
4 订单总额:150.0

最后一行总金额本应 输出190.00 ,但是却输出了150.00。 这里就演示了类的使用者通过使用集合本身的add方法向集合中添加元素,而没有使用addOrderItem方法,最后导致了订单中总金额出现了错误。 因此我们需要对此代码进行重构:让使用者只能遍历集合,而不能对集合进行任何的修改的操作。

重构

【订单案例】 把Order类中的getOrderItems方法的返回值由原来的List更改为Iterable。 下面贴出修改后的代码,没有改变的代码将不会被贴出

/**
 * 订单管理类
 */
public class Order {
    // ...省略未修改的代码

    /**
     * 获取订单列表
     *
     * @return 订单列表
     */
    public Iterable<OrderItem> getOrderItems() {
        return orderItems;
    }
}

public class Test {
    public static void main(String[] args) {
        Order order = new Order();
        order.addOrderItem(new OrderItem(50));
        order.addOrderItem(new OrderItem(100));
        Iterable<OrderItem> orderItems = order.getOrderItems();
        // orderItems.add(new OrderItem(40)); 此行以及无法编译通过
        order.addOrderItem(new OrderItem(40));

        int index = 1;
        for (OrderItem orderItem : orderItems) {
            System.out.println("订单" + index + "价格: " + orderItem.getPrice());
        }
        System.out.println("订单总额:" + order.getOrderTotal());
    }
}

测试结果

1 订单1价格: 50.0
2 订单1价格: 100.0
3 订单1价格: 40.0
4 订单总额:190.0