java设计模式学习笔记(二)--- 结构型模式

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/nayi_224/article/details/82855241

适配器模式

适配器是一个接口转换器,用于在接收不同的输入时,得到一致的输出。
在java中,可以这样理解:使拥有不同接口的实现类,在同一个接口下实现一致的输出。

一般来说,适配器模式并不是在开发初期需要考虑的设计模式。因为这时候我们只会有一套工具类,不需要做适配。但是随着项目的推进,可能会加入一些其他的包。若想让它们实现同一个接口,就可以考虑使用适配器模式了。

适配器的实现方法不唯一,只要实现让适配器类在不同输入下完成一致的输出即可。具体是用继承还是持有对象的方法,可以视情况而定。

package blog.java.pattern.creater.adapter;

public class AdapterTest{
	public static void main(String[] args) {//测试
		System.out.println(new CommonAdapter_forExtend().doSomething());
		System.out.println(new CommonAdapter_forInclude(new NewClass()).doSomething());
	}
}

interface IOld{public int doSomething();}  //老接口
interface INew{public int doAnything();}   //新接口

class OldClass implements IOld{	//老实现类
	public int doSomething() {
		return 1;
	}}
class NewClass implements INew{ //新实现类
	public int doAnything() {
		return 2;
	}}
/**
实用类继承的方法来实现。使NewClass拥有了IOld的方法。
 */
class CommonAdapter_forExtend extends NewClass implements IOld{
	public int doSomething() {//新的实现。内容可能会根据需要做一些更改。
		return super.doAnything() * 2;
	}
}
/**
持有对象的方式实现
 */
class CommonAdapter_forInclude implements IOld{

	private IOld iOld;
	private INew iNew;
	
	CommonAdapter_forInclude(IOld iOld){
		this.iOld = iOld;
	}
	
	CommonAdapter_forInclude(INew iNew){
		this.iNew = iNew;
	}
	
	public int doSomething() {
		if(this.iOld != null)
			return this.iOld.doSomething();
		else
			return this.iNew.doAnything() * 2;
	}
	
}

观察代码可以发现,在适配过程中,无论是OldClass 还是 NewClass 都不需要做修改便实现了目的,这就是适配器写法的作用。
继承是一个很方便的机制,但是在大多数情况下都很危险,慎用!

组合模式

一句话来概括:对一组接口“提取公因式”。

恩,没了。树形结构?那是什么,我不知道。

对于一组类所持有的接口,若它们有共同的方法,可以尝试将这些共有的方法提取出来组建新的接口。写一个简单的例子。

package blog.java.pattern.composite;

public class CompositeTest {}

interface IA{
	public void doA();
	public void doCommon();}
interface IB{
	public void doB();
	public void doCommon();}
interface IC{
	public void doC();
	public void doCommon();}
//↑↑↑↑↑原来的设计↑↑↑↑↑

//↓↓↓↓↓↓新的设计↓↓↓↓↓↓
interface IA_New{
	public void doA();}
interface IB_New{
	public void doB();}
interface IC_New{
	public void doC();}
interface ICommon{
	public void doCommon();}

对于这种结构的接口,一个很自然的想法就是将共有的方法写到一个父类中,这就是组合模式最基本的写法。这部分的代码就不写了。

再来看一下上面的写法(IA,IB,IC的部分),将同样的方法放到不同的接口,这无疑是冗余。对于这样的类,不只是第一次编写,在日后的维护中都会成为大麻烦。

那么什么样的一组类会拥有这种形式的接口呢?所有类都有共性,很明显,那就是有所属关系的一组类。当这组类有更复杂的层次时,也就是说拥有多层的所属关系时,这种结构又被称为树形结构。这也就是组合模式通常的使用场景。

很多文章都是从“树形结构”出发去讲解组合模式的,不过换个角度看,这只是一种最基本思想(消除冗余)的具体实现罢了。

装饰模式

对于一个方法,如果想要动态的在这个方法的执行前后添加多个方法,可以考虑使用装饰模式。

实现方法简单来说就是,装饰类与被装饰类实现同一个接口,并且持有一个被实现类的对象。装饰类在自己的实现方法中调用内部的被装饰类对象的方法,并在此前后添加自己特有的方法。
由于装饰类拥有一样的接口,它们之间可以相互调用。这就导致了它的写法与执行顺序看起来比较难懂。它的执行就像一种抽象的递归一样,在同接口的类之间递归调用。

package blog.java.pattern.decorator;

public class DecoratorTest {
	public static void main(String[] args) {
		IInterface ii = new BaseClass();
		ii = new DecoratorBefore(ii);
		ii = new DecoratorAfter2(ii);
		ii = new DecoratorAfter(ii);
		ii.baseMethod();
		
		System.out.println("-------------------");
		//与上面的写法等效,按喜好选择。
		new DecoratorAfter(new DecoratorAfter2(new DecoratorBefore(new BaseClass()))).baseMethod();
	}
}

interface IInterface{public void baseMethod();} //待装饰方法接口

class BaseClass implements IInterface{
	public void baseMethod() {	//待装饰方法
		System.out.println("do base method ");
	}
}

abstract class Decorator extends BaseClass{
	private IInterface iInterface;
	public Decorator(IInterface iInterface){
		this.iInterface = iInterface;
	}
	
	public void baseMethod() {	//装饰类共有的方法,提出来放到了父类中。
		this.iInterface.baseMethod();
	}
}

class DecoratorBefore extends Decorator{//装饰类1
	
	public DecoratorBefore(IInterface iInterface) {
		super(iInterface);
	}

	private void doSomethingBefore(){
		System.out.println("do something before ");
	}
	
	public void baseMethod() {
		this.doSomethingBefore();
		super.baseMethod();
	}
}

class DecoratorAfter extends Decorator{//装饰类2
	
	public DecoratorAfter(IInterface iInterface) {
		super(iInterface);
	}

	private void doSomethingAfter(){
		System.out.println("do something1 after ");
	}
	
	public void baseMethod() {
		super.baseMethod();
		this.doSomethingAfter();
	}
}

class DecoratorAfter2 extends Decorator{//装饰类3
	
	public DecoratorAfter2(IInterface iInterface) {
		super(iInterface);
	}

	private void doSomethingAfter(){
		System.out.println("do something2 after ");
	}
	
	public void baseMethod() {
		super.baseMethod();
		this.doSomethingAfter();
	}
}

装饰模式的精髓就在于我不只可以这样写

		IInterface ii = new BaseClass();
		ii = new DecoratorBefore(ii);
		ii = new DecoratorAfter2(ii);
		ii = new DecoratorAfter(ii);
		ii.baseMethod();

还可以这样写

		IInterface ii = new BaseClass();
		ii = new DecoratorBefore(ii);
		ii = new DecoratorAfter2(ii);
		ii = new DecoratorBefore(ii);
		ii = new DecoratorAfter(ii);
		ii = new DecoratorBefore(ii);
		ii.baseMethod();

可以自由的按照需要去组合。

代理模式

我已经写好了一个对象,但是却不准备直接对其进行操作。这时可以考虑使用代理模式。

package blog.java.pattern.proxy;

public class Test {
	public static void main(String[] args) {
		IInterface proxy = new Proxy(new BaseClass());
		proxy.doA();
	}
}

interface IInterface{
	public void doA();
	public void doB();
}
class BaseClass implements IInterface{
	public void doA() {}
	public void doB() {}
}
class Proxy implements IInterface{//代理类
	private IInterface proxy;
	public Proxy(IInterface proxy){
		this.proxy = proxy;
	}
	public void doA() {
		this.proxy.doA();
	}
	public void doB() {
		this.proxy.doB();
	}
}

什么时候使用代理模式

  • BaseClass类是一个不稳定因素。它可能会追加一些新功能(尤其是在某些方法的执行前后添加,就像aop一样),也可能它的代码本身写的很糟糕,我不想直接使用。这时可利用代理模式将调用者与对象解耦的特点,方便修改。
  • 想让BaseClass这样的对象延迟加载。原因与实现方法与单例模式中的几乎一样,就不写实现了。
  • 不能或者不想让调用者直接使用对象。主要出现在远程调用上。

享元模式

通俗点说,享元模式就是一个字面意思上的共享池。
有一组对象,它们包含有一样的内部对象。这时就可以把它们提取出来放入共享池中。

package blog.java.pattern.flyweight;

import java.util.HashMap;
import java.util.Map;

public class Test {
	
	public static void main(String[] args) {
		FlyWeight f1 = Factory.getFlyWeight("f1");
		FlyWeight f2 = Factory.getFlyWeight("f2");
		FlyWeight f3 = Factory.getFlyWeight("f1");
		FlyWeight f4 = Factory.getFlyWeight("f1");
		
		System.out.println(f1.shareObj);
		System.out.println(f2.shareObj);
		System.out.println(f3.shareObj);
		System.out.println(f4.shareObj);
	}
}

class FlyWeight{
	String flag;		//标识对象
	Object shareObj;	//共享对象
	Object unShareObj;	//非共享对象
	public FlyWeight(String flag, Object shareObj) {
		this.flag = flag;
		this.shareObj = shareObj;
	}
}

class Factory{	//工厂类
	private static Map<String, Object> map = new HashMap();
	public static FlyWeight getFlyWeight(String flag){
		Object temp = map.get(flag);
		if(temp == null){
			temp = new Object();
			map.put(flag, temp);
		}
		return new FlyWeight(flag, temp);
	}
}

上面的例子中,假定如果flag相同,那么它们就有一样的shareObj,并且这个shareObj是一个逻辑上允许共享的对象。

在创建4个FlyWeight的实例后,只有2个shareObj的实例存在于内存中。

相比于一般的使用方法,享元模式会使程序变得更复杂,并且几乎不会增强功能性。因此,享元模式主要适用于需要降低内存使用的情况下。

外观模式(门面模式)

我想进行一个复杂操作,但是不想管里面的实现,想要只通过一个调用就能得到结果。这时用外观模式。

这个应该所有人都用过,我们平时写的各种工具类就是一个外观模式。不多说了。

package blog.java.pattern.facade;

public class FacadeTest {
	public static void main(String[] args) {
		new Facade().doSomething("do something");
	}
}

class Facade{
	public void doSomething(Object obj){
		System.out.println("do something A");
		System.out.println("do something B");
		System.out.println(obj);
		System.out.println("do something C");
		System.out.println("do something D");
	}
}

桥梁模式

从写法上看,它跟策略模式没什么区别,就是增加了一个抽象父类。想要达到的效果也一样,就是解耦。
为了让写出的代码看起来更像桥梁模式,需要把抽象出来的,不会改变的部分拿到父类中去。

package blog.java.pattern.bridge;

public class Bridge {
	public static void main(String[] args) {
		BaseClass baseClass = new BaseClass(new MethodClass());
		baseClass.doSomething();
	}
}

interface IInterface{public void doA();}

abstract class AbstractClass{
	IInterface iInterface;
	public AbstractClass(IInterface iInterface){
		this.iInterface = iInterface;
	}
	public void doSomething(){	//抽象出的业务
		System.out.println("do 1");
		this.iInterface.doA();
		System.out.println("do 2");
	}
}

class BaseClass extends AbstractClass{
	public BaseClass(IInterface iInterface) {
		super(iInterface);
	}
}

class MethodClass implements IInterface{//具体实现
	public void doA() {
		System.out.println("do A");
	}
}

一般在BaseClass与MethodClass同时为不安定因素是优先考虑使用桥梁模式。

猜你喜欢

转载自blog.csdn.net/nayi_224/article/details/82855241
今日推荐