适配器模式
适配器是一个接口转换器,用于在接收不同的输入时,得到一致的输出。
在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同时为不安定因素是优先考虑使用桥梁模式。