设计模式使软件工作者可以更加简单方便地复用成功的设计和体系结构。在软件设计中应用设计模式不仅可以满足用户的各种需求,同时也有助于提高软件设计工作效率。设计模式是针对问题的一种解决方案,在软件设计中,选择合理的设计模式,可以根据设计模式来进行工作,进而节约许多不必要的环节,提高工作效率。该课题就以设计模式中结构型设计模式来展开对设计模式的初步了解。
关键字:软件,设计模式,结构型设计模式
结构型设计模式
结构型设计模式包含:适配器模式、桥接模式、组合模式、装饰模式、外观模式、享元模式。本文就以这几种设计模式来展开论述,分别讲述其在某种情景需求中的应用。
适配器模式(Adapter Pattern)
将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作。与生活中电源适配器(充电器)有些类似,咱们家庭电源的电压为220V,而咱们生活中的用电器材基本上都不是220V的,例如手机、电脑、电动车等等。因此需要一个适配器将220V的家庭电压转变为用电器需要的指定电压。根据这一原理,在软件中有了类似的设计模式。
适配器既可以作为类结构型模式,也可以作为对象结构型模式。该设计模式中指的接口指的是广义的接口,代码中的函数或方法集合等。
类适配器的通用表示
对象适配器的通用表示
适配器模式所包含角色
- Target:目标抽象类;② Adapter:适配器类;③ Adaptee:适配器类
适配器模式实例
设计一个模拟动物行为的机器人,在机器人中定义了一系列方法,例如:移动move()、叫喊cry()、改设计改不改变源代码的前提下,使得机器人能像动物一样叫喊、移动。
//Robot.java
package com.hsj.domain;
public interface Robot {
void cry();
void move();
}
//BirdAdapter.java
package com.hsj.domain;
public class BirdAdapter extends Bird implements Robot {
@Override
public void cry() {
System.out.println("机器人模仿:");
super.tweedle();
}
@Override
public void move() {
System.out.println("机器人模仿:");
super.fly();
}
}
//Bird.java
package com.hsj.domain;
public class Bird {
public void tweedle(){
System.out.println("鸟儿叽叽叫...");
}
public void fly(){
System.out.println("鸟儿快快飞...");
}
}
//Config.xml(配置文件)
<?xml version="1.0"?>
<config>
<!-- <className>com.hsj.domain.DogAdapter</className> -->
<className>com.hsj.domain.DirdAdapter</className>
</config>
//Client.java(客户端代码)
package com.hsj.client;
import com.hsj.domain.Robot;
import com.hsj.utils.XMLUtils;
public class TestAdapterDemo01 {
public static void main(String[] args) {
Robot robot=(Robot) XMLUtils.getBean();
robot.cry();
robot.move();
}
}
显示结果
扩展功能
//DogAdapter.java
package com.hsj.domain;
public class DogAdapter extends Dog implements Robot {
@Override
public void cry() {
System.out.println("机器人模仿:");
super.wang();
}
@Override
public void move() {
System.out.println("机器人模仿:");
super.run();
}
}
//Dog.Java
package com.hsj.domain;
public class Dog {
public void wang(){
System.out.println("狗汪汪叫....");
}
public void run(){
System.out.println("狗快快跑...");
}
}
//Config.xml(配置文件)
<?xml version="1.0"?>
<config>
<!-- <className>com.hsj.domain.DogAdapter</className> -->
<className>com.hsj.domain.DogAdapter</className>
</config>
显示结果
适配器模式优点
- 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无需修改原有结构。
- 增加了类的透明性和复用性,提高了适配者的复用性,同一个适配者可以在多个不同的系统中复用。
- 灵活性和扩展性非常好。
- 类适配器模式:置换一些适配者的方法很方便。
- 对象适配器模式:可以把多个不同的适配者适配到同一个目标,还可以适配一个适配者的子类。
适配器模式缺点
类适配器模式
- 一次最多只能适配一个适配者类,不能同时适配多个适配者。
- 适配者类不能为最终类。
- 目标抽象类只能为借口,不能为类。
对象适配器模式
在适配器中置换适配者类的某些方法比较麻烦。
适配器模式应用场景
- 系统需要使用一些现有的类,而这些类的接口不符合系统的需要,甚至没有这些类的源代码。
- 创建一个可以重复使用的类,用于和一些彼此之间没有太大关联的类,包括一些可能在将来引进的类一起工作。
桥接模式(Bridge Patern)
将抽象部分与它的实现部分分离,使它们都可以独立地变化。对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式。用抽象关联取代了传统的多层继承。将类之间的静态继承关系转换为动态的对象组合关系。
桥接模式通用表示
桥接模式所包含的角色
① Abstraction:抽象类;② RefinedAbstraction:扩充抽象类;③Implementor:实现类接口
桥接模式实例
设计大、中、小、三种型号的画笔,能够绘制2-6中不同的颜色,如果用蜡笔的话,需要6-18支;如果用毛笔的话,只需要3支,另加2-6个颜料盒,该实例使用桥接模式模拟毛笔的使用过程。
//Pen.java
package com.hsj.domain;
public abstract class Pen {
protected Color color;
public void setColor(Color color) {
this.color = color;
}
public abstract void draw(String name);
}
//Color.java
package com.hsj.domain;
public interface Color {
void bepaint(String penType,String name);
}
//BigPen.java
package com.hsj.domain;
public class BigPen extends Pen {
@Override
public void draw(String name) {
String penType="大号毛笔绘制";
this.color.bepaint(penType, name);
}
}
//MiddlePen.java
package com.hsj.domain;
public class MiddlePen extends Pen {
@Override
public void draw(String name) {
String penType="中号毛笔绘制";
this.color.bepaint(penType, name);
}
}
//SmallPen.java
package com.hsj.domain;
public class SmallPen extends Pen {
@Override
public void draw(String name) {
String penType="小号毛笔绘制";
this.color.bepaint(penType, name);
}
}
//Back.java
package com.hsj.domain;
public class Black implements Color {
@Override
public void bepaint(String penType, String name) {
System.out.println(penType+"黑色的"+name+".");
}
}
//Red.java
package com.hsj.domain;
public class Red implements Color {
@Override
public void bepaint(String penType, String name) {
System.out.println(penType+"红色的"+name+".");
}
}
//Blue.java
package com.hsj.domain;
public class Blue implements Color {
@Override
public void bepaint(String penType, String name) {
System.out.println(penType+"蓝色的"+name+".");
}
}
//While.java
package com.hsj.domain;
public class White implements Color {
@Override
public void bepaint(String penType, String name) {
System.out.println(penType+"白色的"+name+".");
}
}
//ConfigPen.xml
<?xml version="1.0"?>
<config>
<className>com.hsj.domain.White</className>
<className>com.hsj.domain.SmallPen</className>
</config>
//Client.java
package com.hsj.client;
import com.hsj.domain.Color;
import com.hsj.domain.Pen;
import com.hsj.utils.XMLUtils;
public class TestBridgeDemo01 {
public static void main(String[] args) {
Color color=(Color) XMLUtils.getBean("color");
Pen pen=(Pen) XMLUtils.getBean("pen");
pen.setColor(color);
pen.draw("鲜花");
}
}
显示结果
桥接模式优点
- 分离抽象接口及其实现部分。
- 可以取代多层继承方案,极大地减少了子类的个数。
- 提高了系统的可扩展性,在两个变化维度中任意扩展一个维度,不需要修改原有系统,符合开闭原则。
桥接模式缺点
- 会增加系统的理解与设计难度,由于关联关系建立在抽象层,要求开发者一开始就要针对抽象层进行设计与编程。
- 正确识别出系统中两个独立变化的维度并不是一件容易的事情。
桥接模式引用情景
- 需要在抽象化和具体化之间增加更多的灵活性,避免在两个层次之间建立静态的继承关系
- 抽象部分和实现部分可以以继承的方式独立扩展而互不影响
- 一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立地进行扩展。
- 不希望使用继承或因多层继承导致系统类的个数急剧增加的系统。
组合模式(Composite Pattern)
组合多个对象形成树形结构以表示“部分-整体”的结构层次。组合模式对单个对象(即叶子对象)和组合对象(及容器对象)的使用具有一致性。
对象结构型模式
将对象组织到树形结构中,可以用来描述整体与部分的关系。
结构模式通用表示
组合模式包含角色
- Component:抽象构件;② Leaf:叶子构件;③ Composite:容器构件
组合模式包含透明组合模式和安全组合模式两种。
透明组合模式
抽象构件Component:声明了所有用于管理成员对象的方法,包括add()、remove(),以及getChild()等方法
在客户端看来,叶子对象与容器对象所提供的方法是一致的,客户端可以一致的对待所有的对象
缺点:不够安全,因为叶子对象和容器对象在本质上是有区别的。
透明组合模式通用表示
安全组合模式
抽象构件Component:没有声明任何用于管理成员对象的方法,而是在Compostie类中声明并实现这些方法
对于叶子对象,客户端不可能调用到这些方法
缺点:不够透明,客户端不能完全针对抽象编程,必须有区别地对待叶子构件和容器构件。
安全组合模式通用表示
组合模式实例
组合模式在树形目录结构中有着重要的应用,包含文件和文件夹。在文件夹中可以包含文件,还可以继续包含文件夹;但是文件中不能包含文件或者文件夹。该实例以水果盘与水果模拟来实现:水果盘上可以继续放水果盘和水果;而水果既不能放水果盘也不能放水果。
//MyElement.java
package com.hsj.domain;
public abstract class MyElement {
public abstract void eat();
}
//Plate.java
package com.hsj.domain;
import java.util.ArrayList;
import java.util.List;
public class Plate extends MyElement {
private List<MyElement> list=new ArrayList<>();
public void add(MyElement element){
list.add(element);
}
public void delete(MyElement element){
list.remove(element);
}
@Override
public void eat() {
for(MyElement element:list){
element.eat();
}
}
}
//Apple.java
package com.hsj.domain;
public class Apple extends MyElement {
@Override
public void eat() {
System.out.println("吃苹果");
}
}
//Banana.java
package com.hsj.domain;
public class Banana extends MyElement {
@Override
public void eat() {
System.out.println("吃香蕉");
}
}
//Pear.java
package com.hsj.domain;
public class Pear extends MyElement {
@Override
public void eat() {
System.out.println("吃梨");
}
}
//Client.java
package com.hsj.client;
import com.hsj.domain.Apple;
import com.hsj.domain.Banana;
import com.hsj.domain.MyElement;
import com.hsj.domain.Pear;
import com.hsj.domain.Plate;
public class TestCompositeDemo01 {
public static void main(String[] args) {
MyElement elt1,elt2,elt3,elt4,elt5;
Plate plate1,plate2,plate3;
elt1=new Apple();
elt2=new Pear();
plate1=new Plate();
plate1.add(elt1);
plate1.add(elt2);
elt3=new Banana();
elt4=new Banana();
plate2=new Plate();
plate2.add(elt3);
plate2.add(elt4);
elt5=new Apple();
plate3=new Plate();
plate3.add(plate1);
plate3.add(plate2);
plate3.add(elt5);
plate1.eat();
System.out.println("====");
plate2.eat();
System.out.println("====");
plate3.eat();
}
}
显示结果
组合模式优点
- 可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,让客户端忽略了层次的差异,方便对整个层次结构进行控制。
- 客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码。
- 增加新的容器构件和叶子构件都很方便,符合开闭原则。
- 为树形结构的面向对象实现提供了一种灵活的解决方案。
组合模式缺点
- 在增加新构件时很难对容器中的构件类型进行限制。
组合模式应用情景
- 在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,客户端可以一致地对待它们。
- 在一个使用面向对象语言开发的系统中需要处理一个树形结构。
- 在一个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,需要增加新的类型.
总结
结构型模式中的各个设计模式各有各的优点同时也各有各的缺点。在实际项目中我们根据客户的需求挑选出最适合项目的设计模式,灵活变通,不被设计模式中的局限所限制,让设计模式更好地为我们服务。只有通过大量的实践才能灵活的领会设计模式的核心思想。