6、适配器模式(Adapter)
适配器模式就是指,将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。首先,我们来看看类的适配器模式,先看类图:
核心思想就是:有一个Source类,拥有一个方法,待适配,目标接口时Targetable,通过Adapter类,将Source的功能扩展到Targetable里,看代码:
public class Source {
public void method1() {
System.out.println("this is original method!");
}
}
public interface Targetable {
/* 与原类中的方法相同 */
public void method1();
/* 新类的方法 */
public void method2();
}
public class Adapter extends Source implements Targetable {
@Override
public void method2() {
System.out.println("this is the targetable method!");
}
}
public class AdapterTest {
public static void main(String[] args) {
Targetable target = new Adapter();
target.method1();
target.method2();
}
}
输出:
this is original method!
this is the targetable method!
这样Targetable接口的实现类就具有了Source类的功能。
再来看看对象的适配器模式,基本思路和类的适配器模式相同,只是将Adapter类作修改(名字改成Wrapper),这次不继承Source类,而是持有Source类的实例,以达到解决兼容性的问题。同时修改测试类,看图:
代码如下:
public class Wrapper implements Targetable {
private Source source;
public Wrapper(Source source){
super();
this.source = source;
}
@Override
public void method2() {
System.out.println("this is the targetable method!");
}
@Override
public void method1() {
source.method1();
}
}
public class AdapterTest {
public static void main(String[] args) {
Source source = new Source();
Targetable target = new Wrapper(source);
target.method1();
target.method2();
}
}
输出与第一种一样,只是适配的方法不同而已。
第三种适配器模式是接口的适配器模式,接口的适配器是这样的:有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些,此处为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口的所有的方法,而我们不和原始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。看一下类图:
注:图有错误,Wrapper应该改成Wrapper2
这个很好理解,在实际开发中,我们也常会遇到这种接口中定义了太多的方法,以致于有时我们在一些实现类中并不是都需要。看代码:
public interface Sourceable {
public void method1();
public void method2();
}
public abstract class Wrapper2 implements Sourceable{
public void method1(){}
public void method2(){}
}
public class SourceSub1 extends Wrapper2 {
public void method1(){
System.out.println("the sourceable interface's first Sub1!");
}
}
public class SourceSub2 extends Wrapper2 {
public void method2(){
System.out.println("the sourceable interface's second Sub2!");
}
}
public class WrapperTest {
public static void main(String[] args) {
Sourceable source1 = new SourceSub1();
Sourceable source2 = new SourceSub2();
source1.method1();
source1.method2();
source2.method1();
source2.method2();
}
}
测试输出:
the sourceable interface's first Sub1!
the sourceable interface's second Sub2!
7、装饰模式(Decorator)
装饰模式指的是,动态地给一个对象添加一些额外的职责。要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例,关系图如下:
Source类是被装饰类,Decorator类是一个装饰类,可以为Source类动态的添加一些功能,代码如下:
public interface Sourceable {
public void method();
}
public class Source implements Sourceable {
@Override
public void method() {
System.out.println("the original method!");
}
}
public class Decorator implements Sourceable {
private Sourceable source;
public Decorator(Sourceable source){
super();
this.source = source;
}
@Override
public void method() {
System.out.println("before decorator!");
source.method();
System.out.println("after decorator!");
}
}
public class DecoratorTest {
public static void main(String[] args) {
Sourceable source = new Source();
Sourceable obj = new Decorator(source);
obj.method();
}
}
测试输出:
before decorator!
the original method!
after decorator!
装饰器模式的应用场景:
1、需要扩展一个类的功能。
2、动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删。)
缺点:产生过多相似的对象,不易排错!
补充:java中的io,大量使用了装饰模式。
8、代理模式(Proxy)
代理模式指的是,为其他对象提供一种代理以控制对这个对象的访问。代理模式可通过代理类,替原对象进行一些操作,从而将原对象隔离。先来看看关系图:
根据上文的阐述,代理模式就比较容易的理解了,我们看下代码:
public interface Sourceable {
public void method();
}
public class Source implements Sourceable {
@Override
public void method() {
System.out.println("the original method!");
}
}
public class Proxy implements Sourceable {
private Source source;
public Proxy(){
super();
this.source = new Source();
}
@Override
public void method() {
before();
source.method();
atfer();
}
private void atfer() {
System.out.println("after proxy!");
}
private void before() {
System.out.println("before proxy!");
}
}
public class ProxyTest {
public static void main(String[] args) {
Sourceable source = new Proxy();
source.method();
}
}
输出:
before proxy!
the original method!
after proxy!
代理模式的应用场景:
如果已有的方法在使用的时候需要对原有的方法进行改进(注:装饰模式无法将原方法隔离,所以这个时候要用代理模式),此时有两种办法:
1、修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。
2、就是采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式。
使用代理模式,可以将功能划分的更加清晰,有助于后期维护!
补充:动态代理机制,也是代理模式的体现。详情参考:java中的动态代理
9、外观模式(Facade)
外观模式是指,为子系统中的一组接口提供一个一致的界面, 外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式是一种使用频率非常高的结构型设计模式,它通过引入一个外观角色来简化客户端与子系统之间的交互,为复杂的子系统调用提供一个统一的入口,降低子系统与客户端的耦合度,且客户端调用非常方便。外观模式也叫门面模式。我们以一个计算机的启动过程为例,如图:
我们先看下实现类:
public class CPU {
public void startup(){
System.out.println("cpu startup!");
}
public void shutdown(){
System.out.println("cpu shutdown!");
}
}
public class Memory {
public void startup(){
System.out.println("memory startup!");
}
public void shutdown(){
System.out.println("memory shutdown!");
}
}
public class Disk {
public void startup(){
System.out.println("disk startup!");
}
public void shutdown(){
System.out.println("disk shutdown!");
}
}
public class Computer {
private CPU cpu;
private Memory memory;
private Disk disk;
public Computer(){
cpu = new CPU();
memory = new Memory();
disk = new Disk();
}
public void startup(){
System.out.println("start the computer!");
cpu.startup();
memory.startup();
disk.startup();
System.out.println("start computer finished!");
}
public void shutdown(){
System.out.println("begin to close the computer!");
cpu.shutdown();
memory.shutdown();
disk.shutdown();
System.out.println("computer closed!");
}
}
public class User {
public static void main(String[] args) {
Computer computer = new Computer();
computer.startup();
computer.shutdown();
}
}
输出:
start the computer!
cpu startup!
memory startup!
disk startup!
start computer finished!
begin to close the computer!
cpu shutdown!
memory shutdown!
disk shutdown!
computer closed!
如果我们没有Computer类,那么,CPU、Memory、Disk他们之间将会相互持有实例,产生关系,这样会造成严重的依赖,修改一个类,可能会带来其他类的修改,这不是我们想要看到的,有了Computer类,他们之间的关系被放在了Computer类里,这样就起到了解耦的作用。
10、桥接模式(Bridge)
桥接模式就是指,把抽象部分和实现部分分开,使他们可以各自独立的变化。桥接的用意是:将抽象化与实现化解耦,使得二者可以独立变化。像我们常用的JDBC桥DriverManager一样,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基本不需要动太多的代码,甚至丝毫不用动,原因就是JDBC提供统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接就行了。我们来看看关系图:
实现代码:
先定义接口:
public interface Sourceable {
public void method();
}
分别定义两个实现类:
public class SourceSub1 implements Sourceable {
@Override
public void method() {
System.out.println("this is the first sub!");
}
}
public class SourceSub2 implements Sourceable {
@Override
public void method() {
System.out.println("this is the second sub!");
}
}
定义一个桥,持有Sourceable的一个实例:
public abstract class Bridge {
private Sourceable source;
public void method(){
source.method();
}
public Sourceable getSource() {
return source;
}
public void setSource(Sourceable source) {
this.source = source;
}
}
public class MyBridge extends Bridge {
public void method(){
getSource().method();
}
}
测试类:
public class BridgeTest {
public static void main(String[] args) {
Bridge bridge = new MyBridge();
/*调用第一个对象*/
Sourceable source1 = new SourceSub1();
bridge.setSource(source1);
bridge.method();
/*调用第二个对象*/
Sourceable source2 = new SourceSub2();
bridge.setSource(source2);
bridge.method();
}
}
输出:
this is the first sub!
this is the second sub!
这样,就通过对Bridge类的调用,实现了对接口Sourceable的实现类SourceSub1和SourceSub2的调用。下面这个图是我们JDBC连接的类图:
11、组合模式(Composite)
组合模式指的是,将对象组合成树形结构以表示“部分 -整体”的层次结构。有时又叫部分-整体模式,在处理类似树形结构的问题时比较方便,它模糊了简单元素和复杂元素的概念,客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。组合模式可以优化处理递归或分级数据结构。有许多关于分级数据结构的例子,使得组合模式非常有用武之地。先来看看树的关系图:
直接来看代码:
public class TreeNode {
private String name;
private TreeNode parent;
private Vector<TreeNode> children = new Vector<TreeNode>();
public TreeNode(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public TreeNode getParent() {
return parent;
}
public void setParent(TreeNode parent) {
this.parent = parent;
}
//添加孩子节点
public void add(TreeNode node){
children.add(node);
}
//删除孩子节点
public void remove(TreeNode node){
children.remove(node);
}
//取得孩子节点
public Enumeration<TreeNode> getChildren(){
return children.elements();
}
}
public class Tree {
TreeNode root = null;
public Tree(String name) {
root = new TreeNode(name);
}
public static void main(String[] args) {
Tree tree = new Tree("A");
TreeNode nodeB = new TreeNode("B");
TreeNode nodeC = new TreeNode("C");
nodeB.add(nodeC);
tree.root.add(nodeB);
System.out.println("build the tree finished!");
}
}
12、享元模式(Flyweight)
享元模式指的是,运用共享技术有效地支持大量细粒度的对象。享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。
FlyWeightFactory负责创建和管理享元单元,当一个客户端请求时,工厂需要检查当前对象池中是否有符合条件的对象,如果有,就返回已经存在的对象,如果没有,则创建一个新对象,FlyWeight是超类。
在JAVA语言中,String类型就使用了享元模式。String对象是final类型,对象一旦创建就不可改变。在JAVA中字符串常量都是存在常量池中的,JAVA会确保一个字符串常量在常量池中只有一个拷贝。String a="abc",其中"abc"就是一个字符串常量。
享元模式的特点:
- 享元模式以共享的方式高效的支持大量细粒度对象的重用。
- 享元对象能做到共享的关键是区分了内部状态和外部状态。
- 内部状态:可以共享,不会随环境改变而改变。
- 外部状态:不可以共享,会随环境改变而改变。
来看代码:
public interface ChessFlyWeight {
String getColor();//获取颜色,内部状态
void display(Coordinate c);//展示位置,外部状态
}
/**
* 外部状态:棋子坐标位置
*/
public class Coordinate {
private int x,y;
public Coordinate(int x, int y) {
super();
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
class ConcreteChess implements ChessFlyWeight{
private String color;
public ConcreteChess(String color) {
super();
this.color = color;
}
public String getColor() {
return color;
}
public void display(Coordinate c) {
System.out.println("棋子颜色:"+color);
System.out.println("棋子位置"+c.getX()+"-----"+c.getY());
}
}
public class ChessFlyWeightFactory {
//享元池
private static Map<String,ChessFlyWeight> map=new HashMap<String,ChessFlyWeight>();
public static ChessFlyWeight getChess(String color){
if(map.get(color)!=null){
return map.get(color);
}else{
ChessFlyWeight cfw=new ConcreteChess(color);
map.put(color, cfw);
return cfw;
}
}
}
public class Client {
public static void main(String[] args) {
ChessFlyWeight chess1=ChessFlyWeightFactory.getChess("黑色");
ChessFlyWeight chess2=ChessFlyWeightFactory.getChess("黑色");
System.out.println(chess1);
System.out.println(chess2);
System.out.println("-----增加外部状态的处理----");
chess1.display(new Coordinate(10,10));
chess2.display(new Coordinate(20,20));
}
}
输出:
flyweight.ConcreteChess@15db9742
flyweight.ConcreteChess@15db9742
-----增加外部状态----
棋子颜色:黑色
棋子位置10-----10
棋子颜色:黑色
棋子位置20-----20
参考地址:http://blog.csdn.net/zhangerqing/article/details/8239539