软件设计重构秘笈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