GOF23(java设计模式)--结构型模式

适配器模式、代理模式、桥接模式、装饰模式、组合模式、外观模式、享元模式
核心作用:是从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题。
一、适配器模式(adapter)
1.1、作用:将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作。
1.2、模式中的角色:目标接口(Target)、需要适配的类(Adaptee)、适配器(Adapter)
1.3、UML图:


适配器模式.png

1.4、示例:

//目标接口(Target),客户所期待所需要的接口
public interface USB {
    public void connect();
}
class ComputerUSB implements USB,Serializable{
    private String audio;
    private String video;
    private String electric;
    public ComputerUSB(String audio, String video, String electric) {
        super();
        this.audio = audio;
        this.video = video;
        this.electric = electric;
    }
    public String getAudio() {
        return audio;
    }
    public void setAudio(String audio) {
        this.audio = audio;
    }
    public String getVideo() {
        return video;
    }
    public void setVideo(String video) {
        this.video = video;
    }
    public String getElectric() {
        return electric;
    }
    public void setElectric(String electric) {
        this.electric = electric;
    }
    public void connect() {
        System.out.println("USB:"+this.video+this.audio+this.electric+"连接上了电脑");
    }
}
--------------------------------------------------------------------------------
//需要适配的类(Adaptee),需要把VGA转化为USB
public interface VGA {
    public void connect();
}
class ComputerVGA implements VGA,Serializable{
    private String audio;
    private String video;
    private String electric;
    public ComputerVGA(String audio, String video, String electric) {
        super();
        this.audio = audio;
        this.video = video;
        this.electric = electric;
    }
    public String getAudio() {
        return audio;
    }
    public void setAudio(String audio) {
        this.audio = audio;
    }
    public String getVideo() {
        return video;
    }
    public void setVideo(String video) {
        this.video = video;
    }
    public String getElectric() {
        return electric;
    }
    public void setElectric(String electric) {
        this.electric = electric;
    }
    public void connect() {
        System.out.println("VGA:"+this.video+this.audio+this.electric+"连接上了电脑");
    }
}
--------------------------------------------------------------------------------
//适配器(Adapter),通过包装一个需要适配的对象,把原接口转换成目标接口。
public class Adapter extends ComputerVGA implements USB{
    public Adapter(String audio, String video, String electric) {
        super(audio, video, electric);
    }
    public void connect() {
        super.connect();
    }
}
--------------------------------------------------------------------------------
//客户端类
public class Client {
    public static void main(String[] args) {
        USB usb = new Adapter("音频线", "视频线", "电源线");
        usb.connect();
    }
}
--------------------------------------------------------------------------------
结果:
VGA:视频线音频线电源线连接上了电脑
--------------------------------------------------------------------------------
总结:原本只能USB接口才能连上电脑,但结果是VGA接口连上了电脑,这是因为通过适配器把原先的VGA接口转化成了原本需要的电脑USB接口。

二、代理模式(Proxy pattern)
2.1、作用:通过代理,控制对对象的访问。可以详细控制访问某个(某类)对象的方法,在调用这个方法前做前置处理,调用这个方法后做后置处理。(即:AOP的微观实现!)
2.2、UML图:

静态代理模式.png

2.3、示例:
此处略过,详见我的另一篇文章(java代理(静态代理和动态代理):https://www.jianshu.com/p/ed05eef1e60c

三、桥接模式(bridge)
3.1、作用:处理多层继承结构,处理多维度变化的场景,将各个维度设计成独立的继承结构,使各个维度可以独立的扩展在抽象层建立关联。它极大的提高了系统可扩展性,在两个变化维度中任意扩展一个维度,都不需要修改原有的系统,符合开闭原则。
3.2、UML图:


桥接模式.png

3.3、示例:

//电脑接口
public interface Computer {
    public void play();
}
//电脑类
class RealComputer implements Computer{
    protected Brand brand;
    public RealComputer(Brand brand) {
        super();
        this.brand = brand;
    }
    public void play() {
    }
}
//桌面电脑类
class DesktopComputer extends RealComputer{
    public DesktopComputer(Brand brand) {
        super(brand);
    }
    public void play() {
        System.out.println(super.brand.getName()+"桌面电脑开始工作了");
    }
}
//笔记本
class LaptopComputer extends RealComputer{
    public LaptopComputer(Brand brand) {
        super(brand);
    }
    public void play() {
        System.out.println(super.brand.getName()+"笔记本开始工作了");
    }
}
--------------------------------------------------------------------------------
//品牌接口
public interface Brand {
    public void setName(String name) ;
    public String getName();
}
//联想品牌
class LenovoBrand implements Brand{
    public String name;
    public LenovoBrand() {
        super();
        this.name="联想";
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
//华为品牌
class HuaweiBrand implements Brand{
    public String name;
    public HuaweiBrand() {
        super();
        this.name="华为";
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
--------------------------------------------------------------------------------
public class Client {
    public static void main(String[] args) {
        Computer c1 = new DesktopComputer(new LenovoBrand());
        Computer c2 = new DesktopComputer(new HuaweiBrand());
        Computer c3 = new LaptopComputer(new LenovoBrand());
        Computer c4 = new LaptopComputer(new HuaweiBrand());
        c1.play();c2.play();c3.play();c4.play();
    }
}
--------------------------------------------------------------------------------
结果:
联想桌面电脑开始工作了
华为桌面电脑开始工作了
联想笔记本开始工作了
华为笔记本开始工作了
--------------------------------------------------------------------------------
总结:如果出现多个属性组合作为一个对象的唯一标识时(也就是多维度时),用桥接模式,会大大的减少子类的个数,从而降低管理和维护的成本。

四、组合模式(composite)
4.1、作用:把部分和整体的关系用树形结构来表示,从而使客户端可以使用统一的方式处理部分对象和整体对象。组合模式为处理树形结构提供了完美的解决方案。
4.2、关键组成:抽象构件(Component)角色、叶子(Leaf)构件角色、容器(Composite)构件角色
4.3、UML图:


组合模式.png

4.4、示例:

//抽象文件
public interface MyFile {
    public void printChild();
    public String getName();
}
//叶子文件接口
interface LeafFile extends MyFile{
    public void printChild();
    public String getName();
}
//文件夹接口
interface CompositeFile extends MyFile{
    public void printChild();
    public String getName();
    public void addFile(MyFile myFile);
    public void removeFile(MyFile myFile);
}
//文件对象
class MyLeafFile implements LeafFile{
    private String name;
    public MyLeafFile(String name) {
        super();
        this.name = name;
    }
    public void printChild() {
        System.out.println(this.name);
    }
    public String getName() {
        return this.name;
    }
}
//文件夹对象
class MyCompositeFile implements CompositeFile{
    private String name;
    private List<MyFile> childFile;
    public MyCompositeFile(String name) {
        super();
        this.name = name;
        childFile = new ArrayList<MyFile>();
    }
    public void printChild() {
        //System.out.println("文件夹:"+this.name);
        for(MyFile myfile:childFile){
            if(myfile instanceof LeafFile){
                myfile.printChild();
            }else if(myfile instanceof CompositeFile){
                myfile.printChild();
            }
        }
    }
    public String getName() {
        return null;
    }
    public void addFile(MyFile myFile){
        childFile.add(myFile);
    }
    public void removeFile(MyFile myFile){
        childFile.remove(myFile);
    }
}
--------------------------------------------------------------------------------
public class Client {
    public static void main(String[] args) {
        MyFile f1 = new MyLeafFile("文件1.txt");
        MyFile f2 = new MyLeafFile("文件2.avi");
        MyFile f3 = new MyLeafFile("文件3.mp4");
        MyFile f4 = new MyLeafFile("文件4.rar");
        CompositeFile f5 = new MyCompositeFile("文件夹1");
        CompositeFile f6 = new MyCompositeFile("文件夹2");
        CompositeFile f7 = new MyCompositeFile("文件夹3");
        f5.addFile(f1);f5.addFile(f2);
        f6.addFile(f3);f6.addFile(f5);
        f7.addFile(f6);f7.addFile(f4);
        f7.printChild();
    }
}
--------------------------------------------------------------------------------
结果:
文件3.mp4
文件1.txt
文件2.avi
文件4.rar
--------------------------------------------------------------------------------
总结:组合模式,天生有迭代特性,适合树状关系对象的处理

五、装饰模式(decorator)
5.1、作用:能够动态的为一个对象增加新的功能,是一种用于代替继承的技术
5.2、关键组成:Component抽象构件角色、ConcreteComponent 具体构件角色(真实对象)、Decorator装饰角色、ConcreteDecorator具体装饰角色
5.3、UML图:


装饰模式.png

5.4、示例:

//电脑接口
public interface Computer {
    public void play();
}
//普通电脑类
class MyComputer implements Computer{
    public void play() {
        System.out.println("普通电脑");
    }
}
//适配电脑类(中继的作用)
class SuperComputer implements Computer{
    protected Computer computer;
    public SuperComputer(Computer computer) {
        super();
        this.computer = computer;
    }
    public void play() {
        computer.play();
    }
}
//游戏机
class GamerComputer extends SuperComputer{
    public GamerComputer(Computer computer) {
        super(computer);
    }
    public void gamer(){
        System.out.println("我能打游戏");
    }
    public void play() {
        super.computer.play();
        gamer();
    }
}
//办公机
class OfficeComputer extends SuperComputer{
    public OfficeComputer(Computer computer) {
        super(computer);
    }
    public void office(){
        System.out.println("我能办公");
    }
    public void play() {
        super.computer.play();
        office();
    }
}
--------------------------------------------------------------------------------
public class Client {
    public static void main(String[] args) {
        Car car =new Car();
        car.move();
        System.out.println("------------"); 
        FlyCar flyCar = new FlyCar(car);
        flyCar.move();
        System.out.println("------------");
        AICar aiCar = new AICar(car);
        aiCar.move();
        System.out.println("------------");
        AICar aiCar2 = new AICar(flyCar);
        aiCar2.move();
    }
}
--------------------------------------------------------------------------------
结果:
普通电脑
------------
普通电脑
我能打游戏
------------
普通电脑
我能办公
------------
普通电脑
我能打游戏
我能办公
--------------------------------------------------------------------------------
总结:装饰模式可以不通过多层继承自由给指定对象增加新功能

六、外观模式(facade)
6.1、作用:为子系统提供统一的入口。封装子系统的复杂性,便于客户端调用。
6.2、UML图:


外观模式.png

6.3、示例:

public interface 工商局 {
    public void 填写申请();
}
class 武汉工商局 implements 工商局{
    public void 填写申请() {
        System.out.println("填写完了注册公司的申请单");
    }
}
--------------------------------------------------------------------------------
public interface 银行 {
    public void 缴费();
}
class 武汉银行 implements 银行{
    public void 缴费() {
        System.out.println("银行缴费成功");
    }
}
--------------------------------------------------------------------------------
public interface 税务局 {
    public void 交税();
}
class 武汉税务局 implements 税务局{
    public void 交税() {
        System.out.println("税务局交税成功");
    }
}
--------------------------------------------------------------------------------
public class Facade类 {
    public void 注册(){
        工商局 gsj = new 武汉工商局();
        银行 yh = new 武汉银行();
        税务局 swj = new 武汉税务局();
        gsj.填写申请();
        yh.缴费();
        swj.交税();
    }
}
--------------------------------------------------------------------------------
public class 客户端 {
    public static void main(String[] args) {
        Facade类 f = new Facade类();
        f.注册();
    }
}
--------------------------------------------------------------------------------
结果:
填写完了注册公司的申请单
银行缴费成功
税务局交税成功
--------------------------------------------------------------------------------
总结:通过外观模式,把复杂的注册封装为一个体系,暴露出一个Facade类供客户端使用,客户端只用与Facade类打交道,大大降低了客户端的调用复杂度

七、享元模式(FlyWeight)
7.1、作用:享元模式以共享的方式高效地支持大量细粒度对象的重用。
7.2、关键组成:FlyweightFactory享元工厂类、FlyWeight抽象享元类、ConcreteFlyWeight具体享元类、UnsharedConcreteFlyWeight非共享享元类
7.3、优点:a、极大减少内存中对象的数量;b、相同或相似对象内存中只存一份,极大的节约资源,提高系统性能;c、外部状态相对独立,不影响内部状态。
7.4、缺点:a、模式较复杂,使程序逻辑复杂化;b、为了节省内存,共享了内部状态,分离出外部状态,而读取外部状态
使运行时间变长。用时间换取了空间。
7.5、UML图:


享元模式.png

7.6、示例:

public interface 棋子 {
    public 棋子特性 getCharacter();
    public void setCharacter(棋子特性 character);
    public 坐标 getCoordinate();
    public void setCoordinate(坐标 coordinate);
    public void display();
}
class 围棋棋子 implements 棋子{
    private 棋子特性 character;    //ConcreteFlyWeight具体享元类
    private 坐标 coordinate;   //UnsharedConcreteFlyWeight非共享享元类
    public 围棋棋子(棋子特性 character) {
        super();
        this.character = character;
    }
    public 围棋棋子(棋子特性 character, 坐标 coordinate) {
        super();
        this.character = character;
        this.coordinate = coordinate;
    }
    public 棋子特性 getCharacter() {
        return character;
    }
    public void setCharacter(棋子特性 character) {
        this.character = character;
    }
    public 坐标 getCoordinate() {
        return coordinate;
    }
    public void setCoordinate(坐标 coordinate) {
        this.coordinate = coordinate;
    }
    public void display() {
        System.out.println("将"+character.getColor()+"棋子放置到 (X:"+coordinate.getX()+";Y:"+coordinate.getY()+")处");
    }
}
--------------------------------------------------------------------------------
//可共享的对象
public class 棋子特性 {
    private String color;//颜色
    private String shape;//形状
    private String material;//材料
    public 棋子特性(String color) {
        super();
        this.color = color;
        this.shape = "圆形";
        this.material = "木头";
    }
    public 棋子特性(String color, String shape, String material) {
        super();
        this.color = color;
        this.shape = shape;
        this.material = material;
    }
    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
    public String getShape() {
        return shape;
    }
    public void setShape(String shape) {
        this.shape = shape;
    }
    public String getMaterial() {
        return material;
    }
    public void setMaterial(String material) {
        this.material = material;
    }
}
--------------------------------------------------------------------------------
//不可共享的对象
public class 坐标 {
    private int x;
    private int y;
    public 坐标(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;
    };
}
--------------------------------------------------------------------------------
public class 享元工厂 {
    private static HashMap<String,棋子特性> hm = new HashMap<String,棋子特性>();//存储共享对象
    public static 棋子 getChess(String color){
        if(hm.get(color)!=null){
            棋子 newChess = new 围棋棋子(hm.get(color));
            return newChess;
        }else{
            棋子特性 character = new 棋子特性(color);
            棋子 newChess = new 围棋棋子(character);
            hm.put(color, character);
            return newChess;
        }
    }
}
--------------------------------------------------------------------------------
public class 客户端 {
    public static void main(String[] args) {
        围棋棋子 chess1 = (围棋棋子) 享元工厂.getChess("黑色");
        围棋棋子 chess2 = (围棋棋子) 享元工厂.getChess("黑色");
        System.out.println(chess1.getCharacter());
        System.out.println(chess2.getCharacter());
        chess1.setCoordinate(new 坐标(10,10));
        chess1.display();
        chess2.setCoordinate(new 坐标(20,20));
        chess2.display();
        chess1.display();
    }
}
--------------------------------------------------------------------------------
结果:
com.primeton.GOF23.FlyWeight.棋子特性@24c21495
com.primeton.GOF23.FlyWeight.棋子特性@24c21495
将黑色棋子放置到 (X:10;Y:10)处
将黑色棋子放置到 (X:20;Y:20)处
将黑色棋子放置到 (X:10;Y:10)处
--------------------------------------------------------------------------------
总结:此处一定要注意共享的对象与非共享对象的划分,此处是棋子的特性共享,坐标不共享,多个棋子本身是多个对象,只是棋子里面的特性是共享对象,关于这个设计模式的讲解,我个人之前查看过很多文章和教学视频,发现很多讲解着都弄错了(大量的人把棋子本身进行缓存共享了)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,755评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,369评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,799评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,910评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,096评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,159评论 3 411
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,917评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,360评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,673评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,814评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,509评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,156评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,123评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,641评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,728评论 2 351

推荐阅读更多精彩内容

  • 设计模式概述 在学习面向对象七大设计原则时需要注意以下几点:a) 高内聚、低耦合和单一职能的“冲突”实际上,这两者...
    彦帧阅读 3,737评论 0 14
  • 设计模式汇总 一、基础知识 1. 设计模式概述 定义:设计模式(Design Pattern)是一套被反复使用、多...
    MinoyJet阅读 3,922评论 1 15
  • 一、面向对象的六大原则单一职责Single Responsibility Principle,缩写SRP。定义:对...
    为梦想战斗阅读 382评论 0 2
  • 王建德,当上了市建筑局的局长。这使得全局上下,人人张大了嘴巴,一个星期内,全局竟有30多个员工应惊讶,导致下巴脱槽...
    半月仙河北廊坊阅读 393评论 1 5
  • 2017年3月1日,想摆个地摊这个想法又再一次冒了出来,而且一再打压不下去,我不得不停下手头的事情,专心致志处理一...
    小紫馨阅读 256评论 0 0