Java经典设计模式之七大结构型模式(附实例和详解)

2023-10-12 00:59

本文主要是介绍Java经典设计模式之七大结构型模式(附实例和详解),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

博主在大三的时候有上过设计模式这一门课,但是当时很多都基本没有听懂,重点是也没有细听,因为觉得没什么卵用,硬是要搞那么复杂干嘛。因此设计模式建议工作半年以上的猿友阅读起来才会理解的比较深刻。当然,你没事做看看也是没有坏处的。

总体来说设计模式分为三大类:创建型模式、结构型模式和行为型模式。

博主的上一篇文章已经提到过创建型模式,此外该文章还有设计模式概况和设计模式的六大原则。设计模式的六大原则是设计模式的核心思想,详情请看博主的另外一篇文章: Java经典设计模式之五大创建模式(附实例和详解)。

接下来我们看看结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。其中适配器模式主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。其中的对象的适配器模式是各种结构型模式的起源。

一、适配器模式

适配器模式主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。

适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。有点抽象,我们来看看详细的内容。

1.1、类的适配器模式

类的适配器模式核心思想就是:有一个Source类,拥有一个方法,待适配,目标接口是Targetable,通过Adapter类,将Source的功能扩展到Targetable里。

package com.model.structure;public class Source {public void method1() {  System.out.println("this is original method!");  }  
}
package com.model.structure;public interface Targetable {/* 与原类中的方法相同 */public void method1();/* 新类的方法 */public void method2();
}
package com.model.structure;public class Adapter extends Source implements Targetable {public void method2() {System.out.println("this is the targetable method!");}
}
package com.model.structure;public class AdapterTest {public static void main(String[] args) {Targetable target = new Adapter();target.method1();target.method2();}
}

AdapterTest的运行结果:

这里写图片描述

1.2、对象的适配器模式

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

package com.model.structure;public class Wrapper implements Targetable {private Source source;public Wrapper(Source source) {super();this.source = source;}@Overridepublic void method2() {System.out.println("this is the targetable method!");}@Overridepublic void method1() {source.method1();}
}
package com.model.structure;public class AdapterTest {public static void main(String[] args) {Source source = new Source();Targetable target = new Wrapper(source);target.method1();target.method2();}
}

运行结果跟类的适配器模式例子的一样。

1.3、接口的适配器模式

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

这里看文字描述已经试够清楚的了,因此就不贴代码实例了。

二、装饰模式

装饰模式:在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。

装饰模式的特点:

(1) 装饰对象和真实对象有相同的接口。这样客户端对象就能以和真实对象相同的方式和装饰对象交互。
(2) 装饰对象包含一个真实对象的引用(reference)
(3) 装饰对象接受所有来自客户端的请求。它把这些请求转发给真实的对象。
(4) 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展。继承不能做到这一点,继承的功能是静态的,不能动态增删。

具体看看代码实例

package com.model.structure;public interface Sourceable {public void method();
}
package com.model.structure;public class Source implements Sourceable {@Overridepublic void method() {System.out.println("the original method!");}
}
package com.model.structure;public class Decorator implements Sourceable {private Sourceable source;public Decorator(Sourceable source) {super();this.source = source;}@Overridepublic void method() {System.out.println("before decorator!");source.method();System.out.println("after decorator!");}
}
package com.model.structure;public class DecoratorTest {public static void main(String[] args) {//(1) 装饰对象和真实对象有相同的接口。这样客户端对象就能以和真实对象相同的方式和装饰对象交互。//(2) 装饰对象包含一个真实对象的引用(reference)//(3) 装饰对象接受所有来自客户端的请求。它把这些请求转发给真实的对象。//(4) 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。//    在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展。//    继承不能做到这一点,继承的功能是静态的,不能动态增删。Sourceable source = new Source();Sourceable obj = new Decorator(source);obj.method();}
}

运行结果:

before decorator!
the original method!
after decorator!

三、代理模式

代理模式就是多一个代理类出来,替原对象进行一些操作。代理类就像中介,它比我们掌握着更多的信息。

具体看看代码实例。

package com.model.structure;public interface Sourceable {public void method();
}
package com.model.structure;public class Source implements Sourceable {@Overridepublic void method() {System.out.println("the original method!");}
}
package com.model.structure;public class Proxy implements Sourceable {private Source source;public Proxy() {super();this.source = new Source();}@Overridepublic void method() {before();source.method();atfer();}private void atfer() {System.out.println("after proxy!");}private void before() {System.out.println("before proxy!");}
}
package com.model.structure;public class ProxyTest {public static void main(String[] args) {Sourceable source = new Proxy();source.method();}
}

运行结果:

before proxy!
the original method!
after proxy!

四、外观模式

外观模式是为了解决类与类之间的依赖关系的,像spring一样,可以将类和类之间的关系配置到配置文件中,而外观模式就是将他们的关系放在一个Facade类中,降低了类类之间的耦合度,该模式中没有涉及到接口。

我们以一个计算机的启动过程为例,看看如下的代码:

package com.model.structure;public class CPU {public void startup() {System.out.println("cpu startup!");}public void shutdown() {System.out.println("cpu shutdown!");}
}
package com.model.structure;public class Disk {public void startup() {System.out.println("disk startup!");}public void shutdown() {System.out.println("disk shutdown!");}
}
package com.model.structure;public class Memory {public void startup() {System.out.println("memory startup!");}public void shutdown() {System.out.println("memory shutdown!");}
}
package com.model.structure;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!");}
}
package com.model.structure;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!

五、桥接模式

在软件系统中,某些类型由于自身的逻辑,它具有两个或多个维度的变化,那么如何应对这种“多维度的变化”?如何利用面向对象的技术来使得该类型能够轻松的沿着多个方向进行变化,而又不引入额外的复杂度?这就要使用Bridge模式。

在提出桥梁模式的时候指出,桥梁模式的用意是”将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化”。这句话有三个关键词,也就是抽象化、实现化和脱耦。

抽象化:存在于多个实体中的共同的概念性联系,就是抽象化。作为一个过程,抽象化就是忽略一些信息,从而把不同的实体当做同样的实体对待。
实现化:抽象化给出的具体实现,就是实现化。
脱耦:所谓耦合,就是两个实体的行为的某种强关联。而将它们的强关联去掉,就是耦合的解脱,或称脱耦。在这里,脱耦是指将抽象化和实现化之间的耦合解脱开,或者说是将它们之间的强关联改换成弱关联。

下面我们来看看代码实例:

package com.model.structure;public interface Driver {  public void connect();  
}  
package com.model.structure;public class MysqlDriver implements Driver {@Overridepublic void connect() {System.out.println("connect mysql done!");}
}
package com.model.structure;public class DB2Driver implements Driver {@Overridepublic void connect() {System.out.println("connect db2 done!");}
}
package com.model.structure;public abstract class DriverManager {private Driver driver;public void connect() {driver.connect();}public Driver getDriver() {return driver;}public void setDriver(Driver driver) {this.driver = driver;}}
package com.model.structure;public class MyDriverManager extends DriverManager {public void connect() {super.connect();}}
package com.model.structure;public class Client {public static void main(String[] args) {DriverManager driverManager = new MyDriverManager();Driver driver1 = new MysqlDriver();driverManager.setDriver(driver1);driverManager.connect();Driver driver2 = new DB2Driver();driverManager.setDriver(driver2);driverManager.connect();}
}

执行结果:

connect mysql done!
connect db2 done!

如果看完代码实例还不是很理解,我们想想如下两个维度扩展:(1)假设我想加一个OracleDriver,这是一个维度,很好理解,不多解释。(2)假设我们想在连接前后固定输出点什么,我们只需要加一个MyDriverManager2,代码如下:

package com.model.structure;public class MyDriverManager2 extends DriverManager {public void connect() {System.out.println("before connect");super.connect();System.out.println("after connect");}}

再将Client代码中的MyDriverManager 改成 MyDriverManager2 ,执行结果如下:

before connect
connect mysql done!
after connect
before connect
connect db2 done!
after connect

六、组合模式

组合模式,将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。掌握组合模式的重点是要理解清楚 “部分/整体” 还有 ”单个对象“ 与 “组合对象” 的含义。

组合模式让你可以优化处理递归或分级数据结构。

《设计模式》:将对象组合成树形结构以表示“部分整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

涉及角色:

Component:是组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component子部件。

Leaf:在组合中表示叶子结点对象,叶子结点没有子结点。

Composite:定义有枝节点行为,用来存储子部件,在Component接口中实现与子部件有关操作,如增加(add)和删除(remove)等。

比如现实中公司内各部门的层级关系,请看代码:

Component:是组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component子部件。

package com.model.structure;public abstract class Company {private String name;public Company() {}public Company(String name) {super();this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}protected abstract void add(Company company);protected abstract void romove(Company company);protected abstract void display(int depth);}

Composite:定义有枝节点行为,用来存储子部件,在Component接口中实现与子部件有关操作,如增加(add)和删除(remove)等。

package com.model.structure;import java.util.ArrayList;
import java.util.List;public class ConcreteCompany extends Company {private List<Company> cList;public ConcreteCompany() {cList = new ArrayList();}public ConcreteCompany(String name) {super(name);cList = new ArrayList();}@Overrideprotected void add(Company company) {cList.add(company);}@Overrideprotected void display(int depth) {StringBuilder sb = new StringBuilder("");for (int i = 0; i < depth; i++) {sb.append("-");}System.out.println(new String(sb) + this.getName());for (Company c : cList) {c.display(depth + 2);}}@Overrideprotected void romove(Company company) {cList.remove(company);}
}

Leaf:在组合中表示叶子结点对象,叶子结点没有子结点。

package com.model.structure;public class HRDepartment extends Company {public HRDepartment(String name) {super(name);}@Overrideprotected void add(Company company) {}@Overrideprotected void display(int depth) {StringBuilder sb = new StringBuilder("");for (int i = 0; i < depth; i++) {sb.append("-");}System.out.println(new String(sb) + this.getName());}@Overrideprotected void romove(Company company) {}
}
package com.model.structure;public class FinanceDepartment extends Company {public FinanceDepartment(String name) {super(name);}@Overrideprotected void add(Company company) {}@Overrideprotected void display(int depth) {StringBuilder sb = new StringBuilder("");for (int i = 0; i < depth; i++) {sb.append("-");}System.out.println(new String(sb) + this.getName());}@Overrideprotected void romove(Company company) {}
}

Client:

package com.model.structure;public class Client {public static void main(String[] args) {Company root = new ConcreteCompany();root.setName("北京总公司");root.add(new HRDepartment("总公司人力资源部"));root.add(new FinanceDepartment("总公司财务部"));Company shandongCom = new ConcreteCompany("山东分公司");shandongCom.add(new HRDepartment("山东分公司人力资源部"));shandongCom.add(new FinanceDepartment("山东分公司账务部"));Company zaozhuangCom = new ConcreteCompany("枣庄办事处");zaozhuangCom.add(new FinanceDepartment("枣庄办事处财务部"));zaozhuangCom.add(new HRDepartment("枣庄办事处人力资源部"));Company jinanCom = new ConcreteCompany("济南办事处");jinanCom.add(new FinanceDepartment("济南办事处财务部"));jinanCom.add(new HRDepartment("济南办事处人力资源部"));shandongCom.add(jinanCom);shandongCom.add(zaozhuangCom);Company huadongCom = new ConcreteCompany("上海华东分公司");huadongCom.add(new HRDepartment("上海华东分公司人力资源部"));huadongCom.add(new FinanceDepartment("上海华东分公司账务部"));Company hangzhouCom = new ConcreteCompany("杭州办事处");hangzhouCom.add(new FinanceDepartment("杭州办事处财务部"));hangzhouCom.add(new HRDepartment("杭州办事处人力资源部"));Company nanjingCom = new ConcreteCompany("南京办事处");nanjingCom.add(new FinanceDepartment("南京办事处财务部"));nanjingCom.add(new HRDepartment("南京办事处人力资源部"));huadongCom.add(hangzhouCom);huadongCom.add(nanjingCom);root.add(shandongCom);root.add(huadongCom);root.display(0);}
}

运行结果:

北京总公司
--总公司人力资源部
--总公司财务部
--山东分公司
----山东分公司人力资源部
----山东分公司账务部
----济南办事处
------济南办事处财务部
------济南办事处人力资源部
----枣庄办事处
------枣庄办事处财务部
------枣庄办事处人力资源部
--上海华东分公司
----上海华东分公司人力资源部
----上海华东分公司账务部
----杭州办事处
------杭州办事处财务部
------杭州办事处人力资源部
----南京办事处
------南京办事处财务部
------南京办事处人力资源部

七、享元模式

享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。

一提到共享池,我们很容易联想到Java里面的JDBC连接池,想想每个连接的特点,我们不难总结出:适用于作共享的一些个对象,他们有一些共有的属性,就拿数据库连接池来说,url、driverClassName、username、password及dbname,这些属性对于每个连接来说都是一样的,所以就适合用享元模式来处理,建一个工厂类,将上述类似属性作为内部数据,其它的作为外部数据,在方法调用时,当做参数传进来,这样就节省了空间,减少了实例的数量。

看下数据库连接池的代码:

package com.model.structure;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Vector;public class ConnectionPool {private Vector<Connection> pool;/* 公有属性 */private String url = "jdbc:mysql://localhost:3306/test";private String username = "root";private String password = "root";private String driverClassName = "com.mysql.jdbc.Driver";private int poolSize = 100;private static ConnectionPool instance = null;Connection conn = null;/* 构造方法,做一些初始化工作 */private ConnectionPool() {pool = new Vector<Connection>(poolSize);for (int i = 0; i < poolSize; i++) {try {Class.forName(driverClassName);conn = DriverManager.getConnection(url, username, password);pool.add(conn);} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException e) {e.printStackTrace();}}}/* 返回连接到连接池 */public synchronized void release() {pool.add(conn);}/* 返回连接池中的一个数据库连接 */public synchronized Connection getConnection() {if (pool.size() > 0) {Connection conn = pool.get(0);pool.remove(conn);return conn;} else {return null;}}
}

通过连接池的管理,实现了数据库连接的共享,不需要每一次都重新创建连接,节省了数据库重新创建的开销,提升了系统的性能!

注,本文参考了另外一位博主的文章,某些地方有结合自己的一些理解加以修改:
http://blog.csdn.net/zhangerqing/article/details/8194653

这篇关于Java经典设计模式之七大结构型模式(附实例和详解)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/192123

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听