软件构造设计模式与健壮性正确性总结

结构性模式-装饰器模式

在这里插入图片描述

  • 如图所示,上述图形为装饰器模式的一个总体架构。首先有一个顶层接口,下面的部分均为该接口的具体实现,包括一个具体的实现类,装饰器基类。同时,装饰器具体类1和装饰器具体类2是继承装饰器基类的。
  • 装饰器基类的结构应该如下所示:
    在这里插入图片描述
  • 装饰器基类的构造函数应该使其成员变量指向一个特定的接口的实现对象,具体形式如下:
public 装饰器基类(接口名 对象)
{
	成员变量 = 对象;
}
  • 装饰器基类需要重写接口中的方法。如果重写了方法,则该方法应该具有如下的形式
public 实现的方法名()
{
	成员变量.该方法名()
}
  • 当然也可以将这些方法设置为抽象方法,让装饰器具体类对其进行相对应的重写操作。但这些重写操作中都必须要包含这条语句。
  • 这里尤其需要注意,装饰器具体类是继承装饰器基类的。因此它也是具有装饰器基类的成员变量字段的。
  • 再装饰器具体类中重写接口中的方法时,应该具有如下的形式:
public void 实现的方法名()
{
	成员变量.该方法名();
	填写装饰代码;
}

当然这两者的顺序是可以颠倒的,但是还是需要仔细考量这些顺序一面造成不必要的麻烦。

  • 装饰器具体类中的构造函数应该具有如下的形式:
public 装饰器具体类名(接口名 对象)
{
	super(对象);
	//这条语句实际上将该装饰器具体类的父类字段(即装饰器基类)中的成员变量指向传进来的对象
}

有了这一系列操作,如果我们想要构造一个装饰器具体类,那么我们可以采取如下的形式:

接口名 装饰器好以后的对象名 = new 装饰器具体类1(new 装饰器具体类2(new 装饰器具体类3(new 具体的实现类())))

可以看出,这实际上是一种层层嵌套的模式。
上述过程的执行如下:

  • 如图所示,首先令装饰器具体类3中的成员变量指向这个具体的实现类

在这里插入图片描述

  • 其次再令装饰器具体类2中的成员变量指向装饰器具体类3.
    在这里插入图片描述
  • 同理,令装饰器具体类1中的成员变量指向装饰器具体类2,令最终的对象管理装饰器具体类1;

当需要调用接口中的某个方法时:

  • 如图所示,首先调用装饰器具体类1中的该方法,而装饰器具体类1中的成员变量指向装饰器具体类2,因此调用装饰器具体类2中的该方法,如图所示:
    在这里插入图片描述
  • 装饰器具体类2中的该方法又会调用装饰器具体类3中的该方法,如图所示:
    在这里插入图片描述
  • 此时装饰器具体类3中的成员变量指向具体的实现类,因此调用具体实现类中的该方法:
    在这里插入图片描述
  • 具体类中实现的该方法不会再调用其他的了,因此执行完毕后,返回装饰器具体类3中该方法对应的下一条语句开始执行装饰器具体类3中的装饰代码,如图所示:
    在这里插入图片描述
  • 当装饰器具体类3中的装饰代码执行完毕后控制返回到装饰器具体类2,开始执行装饰器具体类2中的代码,如图所示:
    在这里插入图片描述
  • 最后,装饰器具体类2中的代码执行完毕后,控制返回到装饰器具体类1开始执行装饰器具体类1中的装饰代码,如图所示:
    在这里插入图片描述

行为模式-策略模式

  • 这个没什么好说的,同一种接口具有多种不同的实现
  • 对象图依赖关系如下图所示:
    在这里插入图片描述
  • 想要使用时,使用接口而不是具体实现类。这样即使以后的实现需要更改,直接传一个更改的具体类即可。

行为模式-模板模式

  • 适用于完成问题需要一系列步骤,但是这个步骤确是不同的,但是步骤本身的顺序又是确定的。
  • 对象图依赖关系如图所示:
    在这里插入图片描述
  • 客户端仅仅依赖于抽象类
  • 客户端调用时,变量声明为抽象类的类型;右边赋值为具体实现类即可。后续可以直接更改,非常灵活
  • 和白盒框架类似(通过继承方式,往框架中填入具体的东西)

行为模式-迭代器模式

  • 用于对于放进容器类中的一组ADT对象进行遍历访问
    两个重要的接口:

  • Iterable接口:提供返回一个特定的Iterator对象的方法。

public iterface Iterable<T>
{
	Iterator<T> iterator();   //用于返回一个迭代器对象
}
public iterface Iterator<E>
{
	boolean hasNext();
	E next();
	void remove();
}
  • 如果想要自定义集合类,那么实现Iterable接口和Iterator接口。Iterator接口的具体实现可以定义在类的内部,以private class的形式进行实现。即对于一个具体类(想要迭代器的),实现Iterable接口,再将Iterator方法委托给Iterator接口中给定的方法进行实现。对象图如图所示:
    在这里插入图片描述
  • for,each循环中隐含的调用了迭代器
    在这里插入图片描述

行为模式-访问者模式

  • 类似于黑盒框架,伸出一个橄榄枝,在这个橄榄枝上添加东西可以扩充类的行为
  • 具有双向委托的特性
    在这里插入图片描述
  • 欲操作的对象接口中含有如下形式的方法,所以其实现类都必须实现这个接口
public void accept(visitor接口名 参数名);
  • 同时针对于visitor的接口,含有如下的方法,所以其子类都必须进行实现,这里利用了重载的技术:
public void visit(具体实现类1 参数名);
public void visit(具体实现类2 参数名);
//应包含所有的具体实现类
。。。。。。

对于每个visit方法在子类中进行相对应的重写。

  • 在具体的实现类中,每个accept方法的作用是将一个特定的visitor与自己进行相对应的绑定,并实现一系列特定的操作
public void accept(visitor 参数名)   //对应于接口中的参数名,可以传进不同的实现
{
	参数名.visit(this);   //当调用这条语句时,实际上开始调用参数名对应具体实现类对应于该具体实现类所应该进行的操作
}
  • 讲义上的一个比较经典的例子,还应用到了协变的方式,代码十分简洁。
public interface ShoppingCartVisitor { 
int visit(Book book); 
int visit(Fruit fruit); 
}   //visitor接口
public class ShoppingCartVisitorImpl implements ShoppingCartVisitor {   //visitor接口的具体实现
public int visit(Book book) {  //针对于每个实现类实现visitor方法
int cost=0;
if(book.getPrice() > 50){
cost = book.getPrice()-5;
}else 
cost = book.getPrice();
System.out.println("Book ISBN::"+book.getIsbnNumber() + " cost ="+cost);
return cost;
}
public int visit(Fruit fruit) {
int cost = fruit.getPricePerKg()*fruit.getWeight();
System.out.println(fruit.getName() + " cost = "+cost);
return cost;
} }





public class ShoppingCartClient {
public static void main(String[] args) {
ItemElement[] items = new ItemElement[]{
new Book(20, "1234"),new Book(100, "5678"),
new Fruit(10, 2, "Banana"), new Fruit(5, 5, "Apple")};   //利用多态性,Book和Fruit都实现了Itemelem的接口
int total = calculatePrice(items);
System.out.println("Total Cost = "+total);
}
private static int calculatePrice(ItemElement[] items) {
ShoppingCartVisitor visitor = new ShoppingCartVisitorImpl();
int sum=0;
for(ItemElement item : items)
sum = sum + item.accept(visitor);
//这里item.accept(visitor)实际上执行语句visitor.visit(this),而this指向的就是这个elem。根据参数类型判断调用具体实现类中哪个visit的策略
return sum;
} }
  • 可以十分灵活的改变visitor针对于每一个实现类的访问策略,不影响到具体的ADT

异常的类型

  • 运行时异常:代码中提前验证就可以避免。例如数组越界,引用空指针。(不会被编译器检查)
  • 非运行时异常:无法控制。例如打开文件的异常。不能像运行时异常一样处理。有可能发生如下的情况:文件在第一次检查时存在,然后立即就消失了,对后续的IO操作造成影响。(会被编译器检查)

处理两种类型的异常

  • 运行时异常:不需要处理。但如果抛出了,证明程序中有潜在的bug;
  • 其他异常:必须进行处理。通过try,catch机制。如图所示:
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值