软构学习——几种设计模式的理解

本文详细解读了软构学习中的创建型模式(如工厂方法和静态工厂)、结构性模式(适配器和装饰器)以及行为类模式(策略、模板、迭代器和访问者)。通过实例演示,阐述了这些模式如何解决接口不兼容、灵活扩展和代码复用问题。
摘要由CSDN通过智能技术生成

软构学习——几种设计模式的理解

1.创建型模式

工厂方法模式

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

​ 其实就是创建一个另外的类,这个类有一个可以返回目标类对象的方法。通过这种方式能够把,对象的创建与对象的名字分离开,用户可以不用知道所需要类的名字,只要需要知道工厂类,以及相关方法即可。除此之外,这种方式还使得更换目标类的具体实现时,不影响客户端代码,也就是说客户端代码不用改。

example

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 );
}

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 );
    }
}

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

静态工厂方法

既可以在ADT内部实现,也可以构造单独的工厂类。

public class SystemTraceFactory{
    public static Trace getTrace() {
    	return new SystemTrace();
    }
}

相比于通过构造器(new)构建对象:

  • 静态工厂方法可具有指定的更有意义的名称 。
  • 不必在每次调用的时候都创建新的工厂对象 。
  • 可以返回原返回类型的任意子类型。

Open-Closed Principle (OCP)

  • 对扩展的开放,对修改已有代码的封闭

2.结构性模式

(1)适配器模式(adapter

  • 将某个类/接口转换为client期望的其他形式
  • 解决类之间接口不兼容的问题
  • 通过增加一个接口,将已存在的子类封装起来,client面向接口编程,从而隐藏了具体子类。

image-20220611142929285image-20220611142959447

example

interface Shape {
void display(int x1, int y1, int x2, int y2);
}

class LegacyRectangle {
	void display(int x1, int y1, int w, int h) {...}
}

class Rectangle implements Shape {
    void display(int x1, int y1, int x2, int y2) {
   		new LegacyRectangle().display(x1, y1, x2-x1, y2-y1); 
    }
}

class Client {
    Shape shape = new Rectangle();
    public display() {
    	shape.display(x1, y1, x2, y2);
    }
}

其实本质上就是对原来的类进行更改,但不想写重复的代码,利用复用的知识,也就是委派或继承,复用原本的功能,增加新的功能。

(2)装饰器模式(decorator

  • 用每个子类实现不同的特性
  • 需要特性的任意组合

以栈类为例,现在我们需要具有各种功能的栈,比如取消操作、打印信息等。

我们还是不想写重复代码,利用继承是否可行?

显然不行,这会组合爆炸,且仍有大量的代码重复。

image-20220611143903070

那么就需要委派的方法了

为对象增加不同侧面的特性,对每一个特性构造子类,通过委派机制增加到对象上,以递归的方式实现。形象的描述就是穿衣服,每穿一层衣服,就多一些功能。

image-20220611144147463

**接口:**定义装饰物执行的公共操作,就是最原本的功能。

**起始对象:**在其基础上增加功能(装饰),将通用的方法放到此对象中,就是有最基础功能的实现类。

**Decorator抽象类:**是所有装饰类的基类,里面包含的成员变量component 指向了被装饰的对象。

example

interface Stack {
    void push(Item e);
    Item pop();
}

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

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();
    }
    ...
}

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
    }
    ...
}

client

Stack t = new SecureStack(new SynchronizedStack(new UndoStack(s));
t.push(e);

从这个例子我们能明显的看出来它是一层一层的套娃的模式。把额外的功能一点一点的加到对象上。

类似的例子还有

  • static List unmodifiableList(List lst);
  • static Set unmodifiableSet( Set set);
  • static Map unmodifiableMap( Map map);

3.行为类模式(Behavioral

(1)策略模式(strategy

有多种不同的算法来实现同一个任务,但需要client根据需要动态切换算法,而不是写死在代码里。

显然继承依然做不到,我们还得需要委派,为不同的实现算法构造抽象接口,利用delegation,运行时动态传入client倾向的算法类实例。

image-20220611145345864image-20220611145409570

其实跟装饰器模式很像,装饰器是一层一层的穿衣服而策略模式是换衣服穿,因此它也比装饰器模式的结构简单。

example

public interface PaymentStrategy {
	public void pay(int amount);
}

    public class CreditCardStrategy implements PaymentStrategy {
    private String name;
    private String cardNumber;
    private String cvv;
    private String dateOfExpiry;
    public CreditCardStrategy(String nm, String ccNum, 
    String cvv, String expiryDate){
        this.name=nm;
        this.cardNumber=ccNum;
        this.cvv=cvv;
        this.dateOfExpiry=expiryDate;
    }
    @Override
    public void pay(int amount) {
    	System.out.println(amount +" paid with credit card");
    }
}

public class PaypalStrategy implements PaymentStrategy {
    private String emailId;
    private String password;
    public PaypalStrategy(String email, String pwd){
        this.emailId=email;
        this.password=pwd;
    }
    @Override
    public void pay(int amount) {
    	System.out.println(amount + " paid using Paypal.");
    }
}

public class ShoppingCart {
    ...
    public void pay(PaymentStrategy paymentMethod){
    int amount = calculateTotal();
    paymentMethod.pay(amount);
    }
}

client

ShoppingCart cart = new ShoppingCart();
Item item1 = new Item("1234",10);
Item item2 = new Item("5678",40);
cart.addItem(item1);
cart.addItem(item2);
//pay by paypal
cart.pay(new PaypalStrategy("myemail@exp.com", "mypwd"));
//pay by credit card
cart.pay(new CreditCardStrategy(“Alice", "1234", "786", "12/18"));

(2)模板模式(template

做事情的步骤一样,但具体方法不同。

共性的步骤在抽象类内公共实现,差异化的步骤在各个子类中实现。模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现。使用继承和重写实现模板模式。

image-20220611150305298
example

public abstract class CarBuilder {
    protected abstract void BuildSkeleton();
    protected abstract void InstallEngine();
    protected abstract void InstallDoor();
    // Template Method that specifies the general logic
    public void BuildCar() { //通用逻辑
        BuildSkeleton();
        InstallEngine();
        InstallDoor();
    }
}

public class PorcheBuilder extends CarBuilder {
    protected void BuildSkeleton() {
    	System.out.println("Building Porche Skeleton");
    }
    protected void InstallEngine() {
    	System.out.println("Installing Porche Engine");
    }
    protected void InstallDoor() {
    	System.out.println("Installing Porche Door");
    }
    }
    public class BeetleBuilder extends CarBuilder {
    protected void BuildSkeleton() {
    	System.out.println("Building Beetle Skeleton");
    }
    protected void InstallEngine() {
    	System.out.println("Installing Beetle Engine");
    }
    protected void InstallDoor() {
    	System.out.println("Installing Beetle Door");
    }
}

public static void main(String[] args) {
    CarBuilder c = new PorcheBuilder();
    c.BuildCar();
    c = new BeetleBuilder();
    c.BuildCar();
}

这也是白盒框架的模式。

(3)迭代器(iterator

客户端希望对放入容器/集合类的一组ADT对象进行遍历访问,而无需关心容器的具体类型 。

也就是说,不管对象被放进哪里,都应该提供同样的遍历方式。

image-20220611150616885

Iterator pattern:让自己的集合类实现Iterable接口,并实现自己的独特Iterator迭代器(hasNext, next, remove),允许客户端利用这个迭代器进行显式或隐式的迭代遍历:

public class Pair<E> implements Iterable<E> {
    private final E first, second;
    public Pair(E f, E s) { first = f; second = s; }
    public Iterator<E> iterator() {
    	return new PairIterator();
    }
    private class PairIterator implements Iterator<E> {
        private boolean seenFirst = false, seenSecond = false;
        public boolean hasNext() { return !seenSecond; }
        public E next() {
        if (!seenFirst) { seenFirst = true; return first; }
        if (!seenSecond) { seenSecond = true; return second; }
        	throw new NoSuchElementException();
    	}
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }
}

(4)Vistor(感觉不知道怎么翻译好

对特定类型object的特定操作(visit),在运行时将二者动态绑定到一起,该操作可以灵活更改,无需更改被visit的类。

本质上:将数据和作用于数据上的某种/些特定操作分离开来。

为ADT预留一个将来可扩展功能的“接入点”,外部实现的功能代码可以在不改变ADT本身的情况下在需要时通过delegation接入ADT。

image-20220611151042519

/* Abstract element interface (visitable) */
public interface ItemElement {
	public int accept(ShoppingCartVisitor visitor);
}
/* Concrete element */
public class Book implements ItemElement{
    private double price;
    ...
    int accept(ShoppingCartVisitor visitor) {
    	visitor.visit(this);
    }
}
public class Fruit implements ItemElement{
    private double weight;
    ...
    int accept(ShoppingCartVisitor visitor) {
    	visitor.visit(this);
    }
 }

/* Abstract visitor interface */
public interface ShoppingCartVisitor { 
    int visit(Book book); 
    int visit(Fruit fruit); 
} 
public class ShoppingCartVisitorImpl implements ShoppingCartVisitor {
    public int visit(Book book) {
        int cost=0;
        if(book.getPrice() > 50){
            cost = book.getPrice()-5;
        }else 
            cost = book.getPrice();
        System.out.println("Book ISBN::"+book.getIsbnNumber() + " cost ="+cost);
        return cost;
	}
    public int visit(Fruit fruit) {
        int cost = fruit.getPricePerKg()*fruit.getWeight();
        System.out.println(fruit.getName() + " cost = "+cost);
        return cost;
    }
}

public class ShoppingCartClient {
    public static void main(String[] args) {
        ItemElement[] items = new ItemElement[]{
        new Book(20, "1234"),new Book(100, "5678"),
        new Fruit(10, 2, "Banana"), new Fruit(5, 5, "Apple")};
        int total = calculatePrice(items);
        System.out.println("Total Cost = "+total);
    }
    private static int calculatePrice(ItemElement[] items) {
        ShoppingCartVisitor visitor = new ShoppingCartVisitorImpl();
        int sum=0;
        for(ItemElement item : items)
        sum = sum + item.accept(visitor);
        return sum;
    }
}

看起来有点晕,其实就是套娃。计算花费的功能类本身就能实现,但这个功能可能现在不需要,提前加上了就很冗余,我只留个接口就显的干净许多。而且还能实现其他功能,同时我原来的代码也不用动。这就是vistor的好处。

vistor VS iterator

二者都是通过delegation建立两个对象的动态联系 – 但是Visitor强调是的外部定义某种对ADT的操作,该操作于ADT自身关系不大(只是访问ADT),故ADT内部只需要开放accept(visitor)即可,client 通过它设定visitor操作并在外部调用。

而Strategy则强调是对ADT内部某些要实现的功能的相应算法的灵活替换。 这些算法是ADT功能的重要组成部分,只不过是delegate到外部strategy类而已。

区别:visitor是站在外部client的角度,灵活增加对ADT的各种不同操 作(哪怕ADT没实现该操作),strategy则是站在内部ADT的角度, 灵活变化对其内部功能的不同配置。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值