您现在的位置是:首页 >技术交流 >软件构造:对设计模式的理解(上)网站首页技术交流

软件构造:对设计模式的理解(上)

Enzo0312 2023-05-14 08:00:02
简介软件构造:对设计模式的理解(上)

目录(上)

1.创建类模式(Creational Pattern)

1.1工厂方法模式(Factory Method pattern)

2.结构型模式(Structure patterns)

2.1适配器(Adapter)

2.2装饰器(Decorator)

1.创建类模式(Creational Pattern)

1.1工厂方法模式(Factory Method pattern)

工厂方法也被称为“虚拟构造器”,定义一个用于创建对象的接口,但让子类决定实例化哪个类工厂方法允许类将实例化推迟到子类。

应用场景当client不知道要创建哪个具体类的实例,或者不想在client代码中指明要具体创建的实例时,用工厂方法。定义一个用于创建对象的接口,让其子类来决定实例化哪一个类,从而使一个
类的实例化延迟到其子类。

举例:

设计一个Trace接口:

public interface Trace 
{
    // turn on and off debugging
    public void setDebug( boolean debug );
    // write out a debug message
    public void debug( String message );
    // write out an error message
    public void error( String message );
}

需要设计两种实现,比如将信息直接打印到屏幕,或者输出到文件,通常的做法是创建两个类继承Trace,分别实现对应的功能:

public class FileTrace implements Trace 
{          
    private PrintWriter pw;
    private boolean debug;
    public FileTrace() throws IOException 
    {
        pw = new PrintWriter( new FileWriter( "t.log" ) );
    }
    public void setDebug( boolean debug ) 
    {
        this.debug = debug;
    }
    public void debug( String message ) 
    {
        if( debug ) 
        {
            pw.println( "DEBUG: " + message );
            pw.flush();
        }
    }
    public void error( String message ) 
    {
        pw.println( "ERROR: " + message );
        pw.flush();
    }
}

public class SystemTrace implements Trace 
{
    private boolean debug;
    public void setDebug( boolean debug ) 
    {
        this.debug = debug;
    }
    public void debug( String message ) 
    {
        if( debug )
        {
            System.out.println( "DEBUG: " + message );
        }        
    }
    public void error( String message ) 
    {
        System.out.println( "ERROR: " + message );
    }
}

client客户端的使用方式:

//... some code ...
Trace log = new SystemTrace();
log.debug( "entering log" );
Trace log2 = new FileTrace();
log.debug(“...”);

这就要求客户端在创建对象时必须指明具体的类,如果有了新的具体类添加时,客户端的代码很可能也要修改;这种做法显然是不值得提倡的。

于是我们采用工厂方法重新实现该功能:

先构造一个接口,由于创建对象:

interface TraceFactory 
{
    Trace getTrace();
    void otherOperation();
}

然后分别设计两个工厂类,两个工厂类都返回对应的具体类

public class SystemTraceFactory implements TraceFactory 
{
    public Trace getTrace() 
    {
        ... //other operations
        return new SystemTrace();
    }
}
public class FileTraceFactory implements TraceFactory 
{
    public Trace getTrace() 
    {
        return new FileTrace();
    }
}

当客户端使用工厂方法来创建实例时,调用工厂类的getTrace()方法就能得到对应的实例,而且在增加新的产品类时,不会影响客户端的程序,只需要增加新的工厂类:

//... some code ...
Trace log1 = new SystemTraceFactory().getTrace();
log1.debug("entering log");
Trace log2 = new FileTraceFactory().getTrace();
log2.debug("...");

在工厂类中还可以通过客户端输入的类型决定创建何种产品:

interface TraceFactory 
{
    Trace getTrace(String type);
    void otherOperation();
}
public class Factory implements TraceFactory 
{
    public getTrace(String type) 
    {
        if(type.equals("file" )
            return new FileTrace();
        else if (type.equals("system")
            return new SystemTrace();
    }
}

2.结构型模式(Structure patterns)

2.1适配器(Adapter)

适配器模式意图将某个类/接口转换为用户期望的其他形式,它可以解决类之间接口不兼容的问题。通过增加一个接口,将以存在的子类封装起来,用户只需面向接口编程,从而隐藏了具体子类。

适配器有两种方式实现:继承委派

 举例:

有一个显示矩形的程序,LegacyRectangle中的display()方法接受左上角坐标(x, y),宽(w),高(h)四个参数来实现功能,但是客户端想要通过传递左上角和右下角的坐标来实现功能;这样就造成了接口与用户期望的不协调,这种不协调就可以通过一个适配器对象利用委派机制来解决:

 程序实现如下:

// Adaptor类实现抽象接口
interface Shape 
{
    void display(int x1, int y1, int x2, int y2);
}
//具体实现方法的适配
class Rectangle implements Shape 
{
    void display(int x1, int y1, int x2, int y2) 
    {
        new LegacyRectangle().display(x1, y1, x2-x1, y2-y1);
    }
}
//原始的类
class LegacyRectangle 
{
    void display(int x1, int y1, int w, int h) {...}
}
//对适配器编程,与LegacyRectangle隔离
class Client 
{
    Shape shape = new Rectangle();
    public display() 
    {
        shape.display(x1, y1, x2, y2);
    }
}

 2.2装饰器(Decorator)

装饰模式允许通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为。假设要为对象增加不同侧面的特性,那么就可以通过装饰器模式来解决,为每一个特性构造子类,通过委派机制增加到对象上。

举例:

假设你想要进行栈(Stack)数据结构的扩展,其中包含以下新特性:

  • UndoStack:一个可以撤消以前的推送或弹出操作的堆栈
  • SecureStack:需要密码的堆栈
  • SynchronizedStack:序列化并发访问的堆栈

我们可以分别实现这些含有特殊方法的栈,但是如果将这些特性中的某几个组合起来呢?

比如,客户端需要既含有密码又可以撤销之前操作的栈UndoSecureStack,这个时候如果重新构造栈

 可以看到,继承树的规模变得很大,其中也包含了大量的代码冗余。

用装饰器改写:

首先实现接口,Stack功能:

interface Stack {
    void push(Item e);
    Item pop();
}
public class ArrayStack implements Stack {
        ... //rep
    public ArrayStack() {...}
    public void push(Item e) {
        ...
    }
    public Item pop() {
        ...
    }
        ...
}

设计Decorator装饰类:

public abstract class StackDecorator implements Stack 
{
    protected final Stack stack;
    public StackDecorator(Stack stack) 
    {
        this.stack = stack;
    }
    public void push(Item e) 
    {
        stack.push(e);
    }
    public Item pop() 
    {
        return stack.pop();
    }
        ...
}

含有新特性的类,通过delegation实现:

public class UndoStack extends StackDecorator
{
    private final UndoLog log = new UndoLog();
    public UndoStack(Stack stack) 
    {
        // 委托
        super(stack);
    }
    public void push(Item e) 
    {
        super.push(e);
        
        // 新特性
        log.append(UndoLog.PUSH, e);
    }
    public void undo() 
    {
        //implement decorator behaviors on stack
    }
        ...
}

客户端想使用既含有撤销功能又含有密码的类:

Stack t = new SecureStack(new SychronizedStack(new UndoStack(s)));

可以很好地满足我们的需求。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。