设计模式学习--02结构模式(适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式)

设计模式是思想,灵活使用会很大程度上减少开发的难度,拓展框架和程序的时候会很方便

23种设计模式中的结构模式有7种:适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
下面就来依次分享讲解各种结构模式。

1适配器模式

适配器模式有点像是转换器,将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。

1.1类的适配器模式(Adapter)

核心思想就是:有一个“视频“类,拥有一个方法,待适配,目标接口是“手机“,通过“智能手机“类,将“视频“的功能扩展到“手机“里面里,看代码:

public class Video {
    public void playVideo(){
        System.out.println("视频播放");
    }
}

public interface Phone {
   public void playVideo();
    public  void call();
}
public class IntelligentPhone extends Video  implements Phone {

    @Override
    public  void call(){
        System.out.println("打电话");
    }

}

//测试类
public class AdapterTest {
    public  static void main(String[] args){
        Phone phone = new IntelligentPhone();
        phone.playVideo();
        phone.call();
    }
}
//输出:
//视频播放
//打电话

这样IntelligentPhone接口的实现类就具有了Video类的功能。
使用一个适配器类来继承目标类来实现适配。

1.2对象的适配器模式

基本思路和类的适配器模式相同,只是将IntelligentPhone类作修改,这次不继承Video类,而是持有Video类的实例,以达到解决兼容性的问题。

public class IntelligentPhone  implements Phone {
    private Video video;
    public IntelligentPhone(Video video){
        super();
        this.video = video;
    }
    @Override
    public  void call(){
        System.out.println("打电话");
    }

    @Override
    public  void playVideo(){
        video.playVideo();
    }
}

//测试类
public class AdapterTest {
    public  static void main(String[] args){
        Video video = new Video();
        Phone phone = new IntelligentPhone(video);
        phone.playVideo();
        phone.call();
    }
}
//输出:
//视频播放
//打电话

适配器的方法和思路不同,但是输出结果是一样的,对象的适配器模式是适配类持有对象(适配的时候NEW对象出来),类的适配器模式(是继承目标类)。

1.3接口的适配器模式

接口的适配器是这样的:有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些,此处为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们不和原始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。

//还是手机的例子
public interface Phone {
   public void playVideo();
    public  void call();
    public void playMusic();
}
//适配抽象类 实现手机接口
public abstract class PhoneAbs implements Phone {
    @Override
    public void playVideo(){
        System.out.println("看视频");
    }

    @Override
    public  void call(){
        System.out.println("打电话");
    }

    @Override
    public void playMusic(){
        System.out.println("放音乐");
    }
}

//实际类 继承适配的抽象类 实现需要的方法
public class VideoPhone extends PhoneAbs {
  
    public void playVideo(){
        System.out.println("看视频");
    }
   
    public  void call(){
        System.out.println("打电话");
    }
}

public class MusicPhone extends PhoneAbs {

    public  void call(){
        System.out.println("打电话");
    }
    public void playMusic(){
        System.out.println("放音乐");
    }

}

总结一下三种适配模式:
类的适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。

对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个适配类,持有原类的一个实例,在适配类的方法中,调用实例的方法就行。

接口的适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类适配类,实现所有方法,我们写别的类的时候,继承抽象类即可。

2装饰模式

说到装饰模式,就不得不讲一下代理模式。这两个设计模式看起来很像。对装饰器模式来说,装饰者(decorator)和被装饰者(decoratee)都实现同一个 接口。对代理模式来说,代理类(proxy class)和真实处理的类(real class)都实现同一个接口。此外,不论我们使用哪一个模式,都可以很容易地在真实对象的方法前面或者后面加上自定义的方法。

然而,实际上,在装饰器模式和代理模式之间还是有很多差别的。装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问。换句话 说,用代理模式,代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模 式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。

装饰器模式的应用场景:

1、需要扩展一个类的功能。

2、动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删。)

上代码(简单点我继续来改手机类把,装饰它):

public interface Phone {
    public  void call();
}

public class VideoPhone implements Phone {

    @Override
    public  void call(){
        System.out.println("打电话");
    }
}
//装饰类
public class DecoratorPhone implements Phone{

    private Phone phone;
    public  DecoratorPhone(Phone phones){
        super();
        phone = phones;
    }
    @Override
    public  void call(){
        //原版
        phone.call();
        //装饰代码
        System.out.println("装饰了");
    }

}
//测试
public class AdapterTest {
    public  static void main(String[] args){
        Phone phone = new VideoPhone();
        Phone DecoratorPhone = new DecoratorPhone(phone);
        DecoratorPhone.call();
    }
}
//输出:
//打电话
//装饰了

需要装饰的类,被装饰着持有,调用原有方法,并且执行装饰后的方法。

3代理模式(Proxy)

用过spring的都知道,代理模式是spring框架的核心之一,框架中使用了大量的代理模式。

spring中的AOP(面向切面编程)和IOC(控制翻转)。

AOP的核心就是代理模式中的动态代理。

至于想了解spring是怎么动态代理的有个核心类ProxyCreatorSupport,自己去看吧,这里只是将设计模式。

上代理模式的代码,和装饰模式很像,最大的区别在ProxyPhone类里面,打了注解仔细看:

public interface Phone {
    public  void call();
}

public class VideoPhone implements Phone {

    @Override
    public  void call(){
        System.out.println("打电话");
    }
}
//代理类
public class ProxyPhone implements Phone{

    private Phone phone;
    //代理模式和装饰模式最大区别 构造方法中不需要传入代理的类,在编译的时候就确定对象
    public  ProxyPhone(){
        super();
        phone = new VideoPhone();
    }
    @Override
    public  void call(){
        //原版
        phone.call();
        //装饰代码
        System.out.println("装饰了");
    }

}
//测试
public class ProxyTest {
    public  static void main(String[] args){
        Phone proxyPhone = new ProxyPhone();
        proxyPhone.call();
    }
}
//输出:
//打电话
//代理后加了方法

4外观模式

外观(Facade)模式又叫作门面模式,是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。

外观模式最典型的应用就是java的三层开发模式,来个简单的外观模式例子,再来理解三层开发吧:

public class Dao {
    public void getData(){
        System.out.println("Dao拿数据");
    }
}
public class Service {
    public void getData(){
        System.out.println("逻辑层处理数据");
    }
}
public class Controller {
    public void getData(){
        System.out.println("控制层处理数据");
    }
}
//外观页面
public class FacadeUI {
    private Controller controller;
    private Service service;
    private Dao dao;

   public FacadeUI(){
        controller = new Controller();
        service = new Service();
        dao = new Dao();
    }
   public void getData(){
       System.out.println("页面拿数据");
       dao.getData();
       service.getData();
       controller.getData();
   }

}
//测试
public class FacadeTest {
    public static  void  main(String[] args){
        FacadeUI ui = new FacadeUI();
        ui.getData();
    }
}
//输出 
//页面拿数据
//Dao拿数据
//逻辑层处理数据
//控制层处理数据

在测试类中其实只是调用了外观页面的getData()方法,表面上看来就一个方法,但是实际后台为了拿数据分别调用了三层逻辑代码。这就是外观模式的最经典应用。

在实际开发中,三层是分开了,但是现在的mvc框架自动封装了’FacadeUI’,程序员们只需要开发三层中的逻辑。

现在的开发大多使用spring和mybatis,封装后外观模式提现的不是那么明显,jdbc取数据也是直接使用框架调用接口。

5桥接模式(Bridge)

桥接模式是将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(interface)模式。–百度百科

桥接的用意是:将抽象化与实现化解耦,使得二者可以独立变化

桥接模式最大的使用示例就是JDBC连接数据库,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基本不需要动太多的代码,甚至丝毫不用动,原因就是JDBC提供统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接就行了。

写一个非常简单的例子来模拟桥接模式:

/**
 * jdbc模拟
 */
public interface JDBCDemo {

    public String getData();
}

//Mysql实现jdbc
public class MysqlData implements JDBCDemo{
    public String getData(){
        System.out.println("Mysql查询数据");
        return "mysql数据";
    }
}
//Oracle实现jdbc
public class OracleData implements JDBCDemo {
    public String getData(){
        System.out.println("Oracle查询数据");
        return "Oracle数据";
    }
}
/**
 * 桥接实体 持有JDBC接口(桥)
 */
public class BridgeExample {
    public JDBCDemo jdbc;
    //这里简单的例子没有使用框架的注入
    public void setJdbc(JDBCDemo jdbc){
        this.jdbc = jdbc;
    }
    public JDBCDemo getJdbc(){
        return  jdbc;
    }
    public String getData(){
        return  jdbc.getData();
    }

}

/**
 * 桥接实体
 */
public class MyBridge extends BridgeExample{
    public String getData(){
        return  getJdbc().getData();
    }
}
public class Test {
    public static void main(String[] args) {

        BridgeExample bridge = new MyBridge();
        /*调用第一个对象*/
        JDBCDemo jdbc = new MysqlData();
        bridge.setJdbc(jdbc);
       String data =  bridge.getData();
        System.out.println(data);

        BridgeExample bridge2 = new MyBridge();
        /*调用第二个对象*/
        JDBCDemo jdbc2 = new OracleData();
        bridge2.setJdbc(jdbc2);
        String data2 =   bridge2.getData();
        System.out.println(data2);
    }
}
//输出:Mysql查询数据
//mysql数据
//Oracle查询数据
//Oracle数据

从例子上可以看出,调用两个数据库其实使用的方法都是一样的,真实数据库实现桥接其实就是每个数据库有不同的driver接口,oracle数据库的是oracle.jdbc.driver.OracleDriver,mysql数据库的是com.mysql.cj.jdbc.Driver。
在JAVA里面使用的增删改查其实都是一样的接口,只是调用数据库的驱动不一样。

6组合模式(Composite)

组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。二叉树就是一个组合模式典型的例子。
写个简单的组合模式吧:

import java.util.ArrayList;
import java.util.List;

public class TreeNode {
    private String name;
    private TreeNode parent;
    private List<TreeNode> children = new ArrayList<TreeNode>();

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    public TreeNode(String name){
        this.name = name;
    }

    public TreeNode getParent() {
        return parent;
    }

    public void setParent(TreeNode parent) {
        this.parent = parent;
    }
    //添加子节点
    public void add(TreeNode childrenNode){
        children.add(childrenNode);
    }
    //删除子节点
    public void remove(TreeNode childrenNode){
        children.remove(childrenNode);
    }
    //拿到子节点
    public List<TreeNode>  getChildren( ){
        return children;
    }
//测试打印 所以复写toString
    @Override
    public String toString() {
        return "TreeNode{" +
                "name='" + name + '\'' +
                ", parent=" + parent +
                ", children=" + children +
                '}';
    }
}

public class NodeTest {
    public static void main(String[] args) {
        TreeNode parent =    new TreeNode("parent");
        TreeNode c1 =    new TreeNode("c1");
        TreeNode c2 =    new TreeNode("c2");
        parent.add(c1);
        parent.add(c2);
        //打印添加
        System.out.println(parent.getChildren().toString());
        parent.remove(c2);
        //删除后再打印
        System.out.println(parent.getChildren().toString());
    }
    //输出:[TreeNode{name='c1', parent=null, children=[]}, TreeNode{name='c2', parent=null, children=[]}]
//[TreeNode{name='c1', parent=null, children=[]}]

简单的树形结构就是这样 树节点知道自己的parent和children,一般遍历树的时候没有parent就是树的初始节点,没有children的就是树结构的末尾节点(看HashMap的源码可以充分提现这样的链表结构树)。

7享元模式(Flyweight)

享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。
当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值