欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java设计模式之结构型模式

程序员文章站 2022-03-26 20:35:35
什么是代理?什么是静态代理?什么是动态代理?举个例子:车站(被代理)需要卖票,为了方便更多的客户,车站在很多地方找了第三方的合作机构进行卖票(这个角色就是代理),票卖出去之后,第三方机构会抽取一定的提成,同时也需要提供除了票以外的一些其他售后服务,在这里第三方机构就是代理。所谓的静态代理就是,全国各地除了车站外还要多设置100个售票的地方,而这100个地方就需要找100个第三方机构来管理和卖,这种形式就静态代理,成本较高。假设找一个第三方机构,让它一个去管理这100个地方的售票和售后服务,在不同的地方切...

结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

一、适配器模式

什么是适配器模式?适配器模式是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。 简单的来说就是通过某个接口将不兼容的两个类进行兼容,俗称转换器。
举例,小明买了一个美国的进口电视,进口的电视需要的电压是110V,而国内的输出电压是220V,现在需要一个转换器(适配器)对国内的220V电压转成电视能用的110V电压。

/**
 * 目标
 */
public interface Target {
	// 转换成110V电压
	public void convertTo110V();
}
/**
 * 源头
 */
public class Source {
	// 输入的是220V电压
	public void input220V() {
		System.out.println("源头是220V电压");
	}
}
/**
 * 转换器
 */
 // (1)类适配器模式
public class ConvertAdapter extends Source implements Target {
	
	// 把原来的220V转成目标110V
	public void convertTo110V() {
		input220V();
		System.out.println("转换中...");
		System.out.println("输出110V");
	}

}
/**
 * 转换器
 */
  // (2)对象适配器模式
public class ConvertAdapter implements Target {
	
	private Source mSource;
	
	public ConvertAdapter(Source source) {
		this.mSource = source;
	}
	
	// 把原来的220V转成目标110V
	public void convertTo110V() {
		mSource.input220V();
		System.out.println("转换中...");
		System.out.println("输出110V");
	}

}

优点
(1)更好的复用性,系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。
(2)解耦性,将目标类和适配者类解耦,通过引入一个适配器类重用现有的适配者类,而无需修改原有代码。
缺点
过多的使用适配器,会让系统非常零乱,不易整体进行把握。

二、装饰者模式

装饰器模式作用是针对目标方法进行增强,提供新的功能或者额外的功能。
而实际上,装饰器模式和代理模式的实现方式基本一致,只在目标的存在上有些差别,这个后面我们具体讲述。

/**
 * 被装饰的目标
 */
public interface House {
	void show();
}

/**
 * House的实现类
 */
public class ZhangSanHouse implements House {

	public void show() {
		System.out.println("这是张三的房子");
	}

}

/**
 * House的实现类
 */
public class LiSiHouse implements House {

	public void show() {
		System.out.println("这是李四的房子");
	}

}
/**
 * 装饰者
 */
public class Decorator implements House {

	private House mHouse;
	
	public Decorator(House house) {
		this.mHouse = house;
	}
	
	public void show() {
		System.out.println("装饰前");
		mHouse.show();
		System.out.println("装饰后");
	}

}
// 客户端调用如下
public static void main(String[] args) {
		House house = new LiSiHouse();
		Decorator decorator = new Decorator(house);
		decorator.show();
}

和代理模式的区别主要是站在应用及思想角度:
(1)装饰模式正如其名,假设你买了一个手机,但是又怕它摔破,于是你在手机上贴膜和加保护套。但这个手机从核心上来说,还是你之前的手机,之前添加了一些装饰,相当于增强自己。
(2)代理模式就像明星找了一个经纪人,经纪人代理了明星的很多工作,你需要找明星的话,必须通过代理人(经纪人)。

三、代理模式

什么是代理?什么是静态代理?什么是动态代理?
举个例子:
车站(被代理)需要卖票,为了方便更多的客户,车站在很多地方找了第三方的合作机构进行卖票(这个角色就是代理),票卖出去之后,第三方机构会抽取一定的提成,同时也需要提供除了票以外的一些其他售后服务,在这里第三方机构就是代理。所谓的静态代理就是,全国各地除了车站外还要多设置100个售票的地方,而这100个地方就需要找100个第三方机构来管理和卖,这种形式就静态代理,成本较高。假设找一个第三方机构,让它一个去管理这100个地方的售票和售后服务,在不同的地方切换不同形式的售后服务,这个叫动态代理。

1、静态代理

由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。

/**
 * 票
 */
public interface Ticket {
	public void sell();
}

/**
 * 被代理者
 *
 */
public class BusStation implements Ticket {
	public void sell() {
		System.out.println("车站里卖票");
	}
}
/**
 * 第三方机构代理1
 */
public class TicketProxy1 implements Ticket {
	private Ticket mTicket; // 被代理者
	
	public TicketProxy1(Ticket ticket) {
		mTicket = ticket;
	}
	
	public void sell() {
		System.out.println("第三方代理机构1收取服务费");
		mTicket.sell();
		System.out.println("第三方代理机构1提供售后服务");
	}
}
/**
 * 第三方机构代理2
 */
public class TicketProxy2 implements Ticket {
	private Ticket mTicket; // 被代理者
	
	public TicketProxy2(Ticket ticket) {
		mTicket = ticket;
	}
	
	public void sell() {
		System.out.println("第三方代理机构2收取服务费");
		mTicket.sell();
		System.out.println("第三方代理机构2提供售后服务");
	}
}
public static void main(String[] args) {
		BusStation busStation = new BusStation(); // 被代理者
		TicketProxy1 proxy1 = new TicketProxy1(busStation); // 代理者1
		TicketProxy2 proxy2 = new TicketProxy2(busStation); // 代理者2
		proxy1.sell(); // 调用代理者1的方法
		proxy2.sell(); // 调用代理者2的方法
}

2、动态代理

在程序运行时,运用反射机制动态创建而成。

/**
 * 票
 */
public interface Ticket {
	public void sell();
}
/**
 * 被代理者,本尊
 *
 */
public class BusStation implements Ticket {
	public void sell() {
		System.out.println("车站里卖票");
	}
}
/**
 * 全部代理
 */
public class AllProxy implements InvocationHandler {
	
	private Object subject;

	//  构造方法,给我们被代理的真实对象赋初值
	public AllProxy(Object subject) {
	    this.subject = subject;
	}

	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		System.out.println("第三方代理机构收取服务费");
		// method表示的被代理对象的接口方法,invoke去调用该方法
		method.invoke(subject, args);
		System.out.println("第三方代理机构提供售后服务");
		return null;
	}

}
public static void main(String[] args) {
		// 被代理者
		BusStation busStation = new BusStation(); 
		// Proxy.newProxyInstance会动态创建出一个代理对象
		// 参数一,classLoader,类加载器
		// 参数二,被代理者提供的接口,表示你们可以代理我了
		// 参数三,InvocationHandler接口实现类,可以理解为动态创建代理对象时的回调
		Ticket ticket = (Ticket) Proxy.newProxyInstance(busStation.getClass()
				.getClassLoader(), busStation.getClass().getInterfaces(),
				new AllProxy(busStation));
		// 调用代理对象的sell方法
		ticket.sell(); 
}

总结如下:

1、静态代理实现较简单,代理类在编译期生成,效率高。缺点是会生成大量的代理类。
2、动态代理不要求代理类和委托类实现同一个接口,但是委托类(被代理类)需要实现接口,代理类需要实现InvocationHandler接口。
3、动态代理要求代理类实现InvocationHandler接口,通过反射代理方法,比较消耗系统性能,但可以减少代理类的数量,使用更灵活。

四、外观模式

外观(Facade)模式的结构比较简单,主要是定义了一个高层接口。它包含了对各个子系统的引用,客户端可以通过它访问各个子系统的功能。

public class SystemA {
	public void doA() {
		System.out.println("SystemA>>>do something");
	}
}

public class SystemB {
	public void doB() {
		System.out.println("SystemB>>>do something");
	}
}

public class SystemC {
	public void doC() {
		System.out.println("SystemC>>>do something");
	}
}
/**
 * 外观模式主类,对外部调用来说,直接调用该类的方法即可。
 * 屏蔽了子系统的调用细节。
 */
public class Facade {
	private SystemA mSystemA;
	private SystemB mSystemB;
	private SystemC mSystemC;

	public Facade() {
		mSystemA = new SystemA();
		mSystemB = new SystemB();
		mSystemC = new SystemC();
	}
	// 供外部调用的方法
	public void show() {
		mSystemA.doA();
		mSystemB.doB();
		mSystemA.doA();
		mSystemC.doC();
	}
}

客户端不需要知道系统内部的复杂联系,整个系统只提供一个“接待员”即可。
优点:
(1)减少了系统的相互依赖。
(2)提高了灵活性。不管系统内部如何变化,只要不影响到外观对象,任你*活动。
(3)提高了安全性。想让你访问子系统的哪些业务就开通哪些逻辑,不在外观上开通的方法,你就访问不到。
缺点:
不符合开不原则,修改很麻烦。当新增一个子系统的时候,需要修改“接待员”类。

五、享元模式

如果在一个系统中存在多个相同的对象,那么只需要共享一份对象的拷贝,而不必为每一次使用都创建新的对象。目的是提高系统性能。上面的概念乍一听好像单例模式其实不是,单例模式只保存一个对象,但是这里可以有很多个不同对象,但是每个对象只有一个实例而已。也就是说享元模式使用了单例模式。

/**
 * 抽象享元类:形状
 */
public abstract class Shape {
    // 绘画
    public abstract void draw();
}

/**
 * 实现类-圆
 */
@Data
public class Circle extends Shape {
    private String color;

    public Circle(String color) {
        this.color = color;
    }

    @Override
    public void draw() {
        System.out.println("画了一个" + this.color + "圆");
    }
}
/**
 * 享元工厂类
 */
public class ShapeFactory {
	// 缓存对象
    private static HashMap<String, Shape> shapeMap = new HashMap();

    public static Shape getShape(String color) {
        Shape shape = shapeMap.get(color);
        if (shape == null) {
            // 没有则创建新的对象
            shape = new Circle(color);
            shapeMap.put(color, shape);
        }
        return shape;
    }

    public static int getSum(){
        return shapeMap.size();
    }
}
// 使用
public class DrawClient{
    public static void main(String[] args) {
        Shape shape1 = ShapeFactory.getShape("红");
        shape1.draw();
        Shape shape2 = ShapeFactory.getShape("灰");
        shape2.draw();
        Shape shape3 = ShapeFactory.getShape("绿");
        shape3.draw();
		Shape shape4 = ShapeFactory.getShape("灰");
        shape4.draw();
        System.out.println("一共绘制了"+ShapeFactory.getSum()+"种颜色的圆形"); // 3
    }
}

优点:
(1)节省内存空间,对于可重复的对象只会被创建一次,对象比较多时,就会极大的节省空间。
(2)提高效率,由于创建对象的数量减少,所以对系统内存的需求也减小,使得速度更快,效率更高。
缺点:
系统的复杂度更高了。需要分离出内部状态和外部状态,如果这两个状态都需要管理就更麻烦了。

本文地址:https://blog.csdn.net/duncan891101/article/details/107851673