结构型模式:
有7种:适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元模式。
6.适配器模式(Adapter):
简单说明:将一个类的接口转换成客户希望的另一个接口,该模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作.
适配器模式按细分可以分为3类:
- 类的适配器模式
- 对象的适配器模式
- 接口的适配器模式
举例说明:
1.类的适配器模式
package adapter;
public class Source {
//源类的方法
public void method1(){
System.out.println("this is source method1");
}
}
package adapter;
public interface Targetable {
//目标接口中的方法
//与Source类中的方法相同
public void method1();
//新类中的方法
public void method2();
}
package adapter;
/**
* 适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题
* 主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式
* @author cx
*
* 第一种:类的适配器模式(Source 类 +Adapter 类+Targetable 接口 +Test类测试)
*/
/**
* 总结一下三种适配器模式的应用场景:
* 类的适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,
创建一个新类,继承原有的类,实现新的接口即可。
对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Wrapper类,
持有原类的一个实例,在Wrapper类的方法中,调用实例的方法就行。
接口的适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,
我们写别的类的时候,继承抽象类即可。
* @author cx
*
*/
public class Adapter extends Source implements Targetable{
public void method2() {
// TODO 自动生成的方法存根
System.out.println("this is the targetable method");
}
}
package adapter;
public class Test {
public static void main(String[] args) {
Targetable targetable=new Adapter();
//这样Targetable 接口的实现类就实现了Source类中的方法
targetable.method1();
targetable.method2();
}
}
测试结果:在目标接口中适配了源类的方法
this is source method1
this is the targetable method
2.对象的适配器模式:用一个委托类(Wrapper去取代继承Source的Adapter类),降低了类与类之间的耦合,采用依赖的方式。
package adapter;
public class Source {
//源类的方法
public void method1(){
System.out.println("this is source method1");
}
}
package adapter;
public interface Targetable {
//目标接口中的方法
//与Source类中的方法相同
public void method1();
//新类中的方法
public void method2();
}
package adapter;
/**
* 对象的适配器模式
* 只是将Adapter 类修改,不继承Source类,而是持有Source类的实例
* @author cx
*
*/
public class Wrapper implements Targetable{
private Source source;
//Wrapper 类为委托类,它持有Source类的实例,委托给Source类去实现
public Wrapper(Source source) {
super();
this.source=source;
}
@Override
public void method1() {
source.method1(); //持有source类的实例,这是对象的适配器模式,而不是类的适配器模式(用继承来获取方法)
}
@Override
public void method2() {
System.out.println("this is the targetable method!");
}
}
package adapter;
public class WrapperTest {
public static void main(String[] args) {
Source source=new Source();
Targetable targetable=new Wrapper(source);
//Targetable 目标接口实现了 Source类中的方法(跨接口连接)
targetable.method1();
targetable.method2();
}
}
测试结果
this is source method1
this is the targetable method!
3.接口的适配器模式:
package adapter;
/**
* 第三种适配器模式:接口的适配器模式;
* 有时我们写的一个接口中有多个抽象方法,当我们写接口实现类时,必须实现该接口的所有方法
* ,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要一些。
* 借助一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们不和原始的接口打交道,只和
* 该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行
* @author cx
*
*/
public interface Sourceable {
public void method1();
public void method2();
}
package adapter;
public class SourceSub1 extends Wrapper2{
//继承一个抽象类,只实现自己想要实现的方法 (实现method1())
@Override
public void method1() {
System.out.println("the sourceable interface is first sub1!");
}
}
package adapter;
public class SourceSub2 extends Wrapper2{
//继承一个抽象类,只实现自己想要实现的方法 (实现method2())
@Override
public void method2() {
System.out.println("the sourceable interface is second sub2!");
}
}
package adapter;
/**
* 第三种:接口的适配器模式,写一个抽象类实现接口中的所有方法,再写一个继承抽象类
* 的子类,只重写我们需要的方法
* @author cx
*
*/
public abstract class Wrapper2 implements Sourceable{
@Override
public void method1() {
// TODO 自动生成的方法存根
}
@Override
public void method2() {
// TODO 自动生成的方法存根
}
}
package adapter;
public class WrapperTest2 {
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 is first sub1!
the sourceable interface is second sub2!
7.装饰器模式(Decorator)
简单说明:动态地给一个对象添加一些额外的职责,就添加功能来说,装饰器模式相比生成子类更为灵活。
举例说明:
package decorator;
public interface Sourceable {
public void method();
}
package decorator;
/**
* Source类是 被装饰类
* @author cx
*
*/
public class Source implements Sourceable{
@Override
public void method() {
// TODO 自动生成的方法存根
System.out.println("the original method!");
}
}
package decorator;
/**
* 装饰模式:就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和
* 被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例
*
* 装饰器模式应用场景:
* 1.需要扩展一个类的功能
* 2.动态的为一个对象增加功能,而且还能动态撤销(继承的功能是静态的,不能动态增删。)
*
* 缺点:产生过多相似的对象,不易排错,同名方法被不同对象持有,有可能搞混
* @author cx
*
*/
public class Decorator implements Sourceable{
private Sourceable sourceable;
public Decorator(Sourceable sourceable) {
super();
this.sourceable=sourceable;
}
@Override
public void method() {
//装饰者给Source.method() 方法再添加一些别的功能,动态的,调用Source.method(),一层又一层的装饰
System.out.println("before decorator!");
sourceable.method(); //装饰一波
System.out.println("after decorator!");
}
}
package decorator;
public class Test {
public static void main(String[] args) {
Sourceable obj=new Decorator(new Source()); //obj.method() --为装饰后的功能方法
obj.method();
}
}
测试结果
before decorator!
the original method!
after decorator!
8.代理模式(Proxy)
简单说明:为其他对象提供一种代理以控制对这个对象的访问,一个类代表另一个类的功能。
举例说明:
package proxy;
public interface Sourceable {
public void method();
}
package proxy;
public class Source implements Sourceable{
@Override
public void method() {
// TODO 自动生成的方法存根
System.out.println("the original method!");
}
}
package proxy;
/**
* 代理模式(Proxy)
* 代理模式就是多一个代理类出来,替原对象进行一些操作,比如打官司时,需要请律师,让他来替我们
* 打官司,因为代理类所掌握的信息比Source类信息全面.
*
* 举例说明:
做一个平台,为了接入非常多的第三方系统,那平台必然需要提供接口。
但问题来了,第三方系统调用平台的接口不一而足,那么如果做一个统一的webservice面向所有的第三方系统,
实际上也是可以的,但是安全性必然不好。
常用的做法是将平台的接口大致分为几种,然后编写几个代理服务,它们分别提供不同的接口服务,
并对不同的第三方厂家进行开放,这样在保证整套系统正常运行的情况下还能在一定程度上提高其安全性。
只是简单补充代理模式的应用场景,其原理就不再细说了。
*
* @author cx
*
*优点:
1)代理模式能将代理对象与真正被调用的对象分离,在一定程度上降低了系统的耦合度。
2)代理模式在客户端和目标对象之间起到一个中介作用,这样可以起到保护目标对象的作用。
代理对象也可以对目标对象调用之前进行其他操作
* 缺点:
* 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
*/
public class Proxy implements Sourceable{
private Source source;
public Proxy() {
super(); //调用父类的构造方法
this.source=new Source();
//注意与装饰者模式的区别:装饰者模式是在构造方法中传了一个Sourceable接口(聚合方式)
//而代理模式是不传接口,而直接实例化Source对象,源类直接交由它这个代理类来实现(组合方式)
}
@Override
public void method() {
//该代理类帮忙完成method()方法
before();
source.method();
after();
}
private void before(){
//由代理类实现不同的功能(相当于交给律师自己办案)
System.out.println("before proxy!");
}
private void after(){
System.out.println("after proxy!");
}
}
package proxy;
public class Test {
public static void main(String[] args) {
//创建一个代理类的对象,保护了原有对象,扩展了一些功能
Sourceable sourceable=new Proxy();
sourceable.method();
}
}
测试结果
before proxy!
the original method!
after proxy!
9.外观模式(Facade)
简单说明:隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。
意图:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
举例说明:
package facade;
public interface Sourceable {
//开启
public void startup();
//关闭
public void shutdown();
}
package facade;
public class CPU implements Sourceable{
@Override
public void startup() {
// TODO 自动生成的方法存根
System.out.println("cpu startup!");
}
@Override
public void shutdown() {
// TODO 自动生成的方法存根
System.out.println("cpu shutdown!");
}
}
package facade;
public class Memory implements Sourceable{
@Override
public void startup() {
// TODO 自动生成的方法存根
System.out.println("memory startup!");
}
@Override
public void shutdown() {
// TODO 自动生成的方法存根
System.out.println("memory shutdown!");
}
}
package facade;
public class Disk implements Sourceable{
@Override
public void startup() {
// TODO 自动生成的方法存根
System.out.println("disk startup!");
}
@Override
public void shutdown() {
// TODO 自动生成的方法存根
System.out.println("disk shutdown!");
}
}
package facade;
/**
* 外观模式:是为了解决类与类之间的依赖关系,像spring一样,可以将类与类之间的关系配置到配置文件中
* 而外观模式就是将他们的关系放在一个Facade(外观类中),降低了类与类之间的耦合度。
*
* @author cx
*
* 若没有Computer类,那么,CPU、Memory、Disk 他们之间将会相互持有实例,产生关系,这样会造成严重的依赖
* 修改一个类,可能会带给其他类的修改,这不是我们想要看到的,有了Computer类,他们的关系就都被放在Computer类中,
* 这样就起到了解耦的作用,这就是外观模式!
*/
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 the computer finished!");
}
public void shutdown(){
System.out.println("shutdown the computer!");
cpu.shutdown();
memory.shutdown();
disk.shutdown();
System.out.println("shutdown the computer finished!");
}
}
package facade;
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 the computer finished!
shutdown the computer!
cpu shutdown!
memory shutdown!
disk shutdown!
shutdown the computer finished!
10.桥接模式(Bridge)
简单说明:是用于把抽象化与实现化解耦,使得二者可以独立变化。
意图:将抽象部分与实现部分分离,使它们都可以独立的变化。
举例说明:笔和颜色分离(不同大小的笔,用不同的颜色(桥接)在一起,画不同的东西),抽象和实现分离!
笔的接口:
package bridge;
public abstract class Pen {
protected Color color;
public void setColor(Color color) {
this.color = color;
}
public abstract void draw(String name);
}
颜色接口:
package bridge;
public interface Color {
public void bePaint(String penType,String name);
}
大型笔:
package bridge;
public class BigPen extends Pen{
@Override
public void draw(String name) {
// TODO 自动生成的方法存根
String penType="大号毛笔绘制";
this.color.bePaint(penType, name);
}
}
中型笔:
package bridge;
public class MiddlePen extends Pen{
@Override
public void draw(String name) {
// TODO 自动生成的方法存根
String penType="中型笔绘制";
this.color.bePaint(penType, name);
}
}
小型笔:
package bridge;
public class SmallPen extends Pen{
@Override
public void draw(String name) {
// TODO 自动生成的方法存根
String penType="小型笔绘制";
this.color.bePaint(penType, name);
}
}
黑色:
package bridge;
public class BlackColor implements Color{
@Override
public void bePaint(String penType, String name) {
// TODO 自动生成的方法存根
System.out.println(penType+"黑色的"+name+".");
}
}
蓝色:
package bridge;
public class BuleColor implements Color{
@Override
public void bePaint(String penType, String name) {
// TODO 自动生成的方法存根
System.out.println(penType+"蓝色的"+name+".");
}
}
客户端测试:
package bridge;
public class Client {
public static void main(String[] args) {
Color color;
Pen pen;
color=new BuleColor(); //蓝色
pen=new BigPen(); //大号绘笔
pen.setColor(color);
//new BigPen().setColor(new BuleColor()).draw("鲜花");//也可以一句话写完
pen.draw("鲜花"); //大号绘笔绘制蓝色鲜花
}
}
测试结果
大号毛笔绘制蓝色的鲜花.
11.组合模式(Composite)
简单说明:将对象组合成树形结构以表示”部分-整体”层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。
举例说明:用树形结构来讲解
(TreeNode)树的子结点类:
package composite;
import java.awt.List;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Vector;
public class TreeNode {
private String name;
private TreeNode parent;
private Vector<TreeNode> children=new Vector<>();
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();
}
}
(Tree)树类,与树的子节点是部分整体关系,因为树由一系列树的结点构成。
package composite;
/**
* 组合模式:组合模式有时又叫部分-整体模式在处理类似树形结构的问题时比较方便
* 耦合程度:组合>聚合>关联>依赖
* 使用场景:将多个对象组合在一起进行操作,常用于表示树形结构中,例如二叉树,树等
* @author cx
*
*/
public class Tree {
TreeNode root=null;
public Tree(String name) {
//组合模式: Tree与TreeNode 共生的关系,所以Tree (整体类) ,TreeNode(部分节点类)
//部分类(TreeNode)中的对象在整体类(Tree)中的构造方法中实例化
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!");
}
}
测试结果
build the tree finished!
12、享元模式(FlyWeight)
简单说明:主要用于减少创建的对象,以减少内存占用和提高性能,有一个池的概念,运用共享技术有效地支持大量细粒度的对象。
举例说明:就拿数据库连接池来举例,(线程池也是采用这个模式).
数据库连接池第一种实现(用Vector类或者ArrayList存储数据库连接池),数据库连接池有一个好处就是不用每次都新建连接,而是直接去连接池找就行了。
package flyweight;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
/**
* 通过连接池的管理,实现了数据库连接的共享,不需要每次都重新创建连接
* 节省了数据库重新创建的开销,提升了系统的性能
* @author cx
*
*/
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 closeConnection(){
//关闭连接时,并不是真正的关闭,而是把这个连接又重新收集回来
pool.add(conn);
}
//返回连接池中的一个数据库连接
public synchronized Connection getConnection(){
if(pool.size()>0){
Connection connection=pool.get(0);
pool.remove(connection);
return connection;
}else{
return null;
}
}
}
数据库连接池的第二种实现(用HashMap存储键值对,键中放标志位)
package flyweight;
import java.security.KeyStore.Entry;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
public class ConnectionPool2 {
private Map<Connection, String> connectionPool=null;
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 void initPool(){
connectionPool =new HashMap<>();
try {
Class.forName(driverClassName);
Connection connection=DriverManager.getConnection(url);
for(int i=0;i<poolSize;i++){
connectionPool.put(conn, "AVAILABLE"); //先创建这么多的连接,并将其放入连接池中
}
} catch (ClassNotFoundException | SQLException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
public Connection getConnection() throws ClassNotFoundException, SQLException{
boolean isConnectionAvailable=true;
for(java.util.Map.Entry<Connection, String> entry:connectionPool.entrySet()){
synchronized (entry) {
if(entry.getValue()=="AVAILABLE"){
entry.setValue("NOTAVAILABLE");
return entry.getKey();
}
isConnectionAvailable=false;
}
}
if(!isConnectionAvailable){
Class.forName(driverClassName);
conn=DriverManager.getConnection(url);
connectionPool.put(conn, "NOTAVAILABLE");
return conn;
}
return null;
}
public void closeConnection(Connection connection){
for(java.util.Map.Entry<Connection, String> entry:connectionPool.entrySet()){
if(entry.getKey().equals(connection)){
entry.setValue("AVAILABLE");
}
}
}
}