#软件构造 面向可维护性的设计模式

    技术2022-07-15  84

    这一节讲了很多设计模式 如下面所示

    目录

    一、创建新实例的模式工厂方法模式抽象工厂模式构造器模式 二、结构模式桥接模式代理模式 三、行为模式Observer模式Visitor 访客模式

    一、创建新实例的模式

    工厂方法模式

    工厂方法模式(Factory Method) 又被称为 Virtual Constructor 虚拟构造器 :当客户端不知道要创建哪个具体类的实例,或者不想在client代码中指明要具体创建的实例时,用工厂方法。定义一个用于创建对象的接口,让其子类来决定实例化哪一个类,从而使一个类的实例化延迟到其子类。

    参考博客https://blog.csdn.net/Franklins_Fan/article/details/105922583

    抽象工厂模式

    抽象工厂模式:提供接口以创建一组相关/相互依赖的对象, 但不需要指明其具体类。

    //AbstractProduct public interface Window { public void setTitle(String s); public void repaint(); public void addScrollbar(); } //ConcreteProductA1 public class PMWindow implements Window { public void setTitle() {} public void repaint() {} } //ConcreteProductA2 public class MotifWindow implements Window { public void setTitle() {} public void repaint() {} }

    使用抽象工厂方法进行构造

    //AbstractFactory public interface AbstractWidgetFactory { public Window createWindow(); public Scrollbar createScrollbar(); } //ConcreteFactory1 //第一个具体产品的工厂方法 public class WidgetFactory1 { public Window createWindow() { return new MSWindow(); } public Scrollbar createScrollbar() { A } } //ConcreteFactory2 //第二个具体产 品的工厂方法 public class WidgetFactory2 { public Window createWindow() { return new MotifWindow(); } public Scrollbar createScrollbar() { B } }

    抽象工厂方法 创建的不是一个完整产品,而是“产品族”(遵循 固定搭配规则的多类产品的实例),得到的结果是:多个不同产品的 object,各产品创建过程对client可见,但“搭配”不能改变。

    实例1: 辅助类:

    public class GUIBuilder { public void buildWindow(AbstractWidgetFactory widgetFactory) { Window window = widgetFactory.createWindow(); Scrollbar scrollbar = widgetFactory.createScrollbar(); window.setTitle("New Window"); window.addScrollbar(scrollbar); } }

    客户端:

    //辅助类 GUIBuilder builder = new GUIBuilder(); //定义抽象工厂接口的实例 AbstractWidgetFactory widgetFactory = null; if(“Motif”) widgetFactory = new WidgetFactory2(); else widgetFactory = new WidgetFactory1(); Window window = widgetFactory.createWindow(); Scrollbar scrollbar = widgetFactory.createScrollbar();

    实例2: 一个StellarSystem(恒星系)由一个Stellar(恒星)和一个行星的列表组成 一个PersonalAppEcosystem 是由用户和移动端应用列表组成

    本质上,抽象工厂方法 把多类产品的工厂方法组合在一起

    构造器模式

    创建的是一个完整的产品,有多个部分组成,client不需了解 每个部分是怎么创建、各个部分怎么组合,最终得到一个产品的完整 object , 而抽象工厂方法创建的不是一个完整产品,而是“产品族” ,构造器模式主要面向客户端要的不是一堆零散的objects (抽象工厂方法那样的结果),而是一个完整的产品。 案例:

    /* "Product" */ class Pizza { private String dough = ""; private String sauce = ""; private String topping = ""; public void setDough(String dough) { this.dough = dough; } public void setSauce(String sauce) { this.sauce = sauce; } public void setTopping(String topping) { this.topping = topping; } }

    其构造器,Builder抽象类有三个抽象方法,分别创建三个 part

    /* "Abstract Builder" */ abstract class PizzaBuilder { protected Pizza pizza; public Pizza getPizza() { return pizza; } public void createNewPizzaProduct() { pizza = new Pizza(); } public abstract void buildDough(); public abstract void buildSauce(); public abstract void buildTopping(); }

    具体的builder子类 中,override三个抽象方法,分别构建三 个parts

    /* "ConcreteBuilder 1" */ class SpicyPizzaBuilder extends PizzaBuilder { public void buildDough() { pizza.setDough("pan baked"); } public void buildSauce() { pizza.setSauce("hot"); } public void buildTopping() { pizza.setTopping("pepperoni+salami"); } }

    对于两个不同的的 builder子类,构建三个parts的内容有差异

    /* "ConcreteBuilder 2" */ class HawaiianPizzaBuilder extends PizzaBuilder { public void buildDough() { pizza.setDough("cross"); } public void buildSauce() { pizza.setSauce("mild"); } public void buildTopping() { pizza.setTopping("ham+pineapple"); } }

    而顾客点单时,不能让顾客自己烤披萨,端盘子(抽象工厂方法产生的零散objects ),而是提供一个完整的产品 于是我们就需要一个Waiter类来提供完整的产品:

    /* "Director" */ class Waiter { private PizzaBuilder pizzaBuilder; public void setPizzaBuilder(PizzaBuilder pb) { pizzaBuilder = pb; } public Pizza getPizza() { return pizzaBuilder.getPizza(); } //在这里具体 构建各部分 public void constructPizza() { pizzaBuilder.createNewPizzaProduct(); pizzaBuilder.buildDough(); pizzaBuilder.buildSauce(); pizzaBuilder.buildTopping(); } }

    客户端案例,实现了构建细节完全对客户端隐藏,顾客不需要知道做一个披萨到底需要什么,怎么组合,只需要选择想要的披萨,找服务员点单即可

    /* A customer ordering a pizza. */ public class PizzaBuilderDemo { public static void main(String[] args) { Waiter waiter = new Waiter(); PizzaBuilder hawaiianPizzabuilder = new HawaiianPizzaBuilder(); PizzaBuilder spicyPizzaBuilder = new SpicyPizzaBuilder(); waiter.setPizzaBuilder( hawaiianPizzabuilder ); waiter.constructPizza(); Pizza pizza = waiter.getPizza(); } }

    二、结构模式

    桥接模式

    将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式,是一种最基本的structural pattern,通过委派+继承的方式建立两个具体类之间的关系。 桥接模式,举个例子:类A需要完成“通讯”功能,它有一个组成部分Device(设备)类,作为抽象接口(模式中的implementor),其有多种实现方式(PC, tablet, phone,即Concrete Implementor),该模式在运行时为类A的具体子类型实例设定具体实现(强调对A和Device两个抽象类的各自实例之间如何建立delegation), 进而在其他各项功能中利用其各自通讯功能。 与之相比的Strategy设计模式则是使用螺丝刀的时候,针对不同的工作任务,选取不同的“刀头”,但目的并非将螺丝刀与刀头组合起来建立永久的delegation, 而只是临时通过delegation完成任务(即调用选择刀头的“算法”),然后二者再无联系。

    案例:

    桥接的接口,各种Shape,希望具备绘图功能,委派到 专门的绘图类

    public interface DrawAPI { public void drawCircle(int radius, int x, int y); }

    桥接的具体实现类

    public class DrawRedCircle implements DrawAPI { @Override public void drawCircle(int radius, int x, int y) { System.out.println(“Color: red " + radius + x + y); } } public class DrawGreenCircle implements DrawAPI { @Override public void drawCircle(int radius, int x, int y) { System.out.println(“Color: green " + radius + x + y); } }

    创造一个使用DrawAPI 的抽象类

    public abstract class Shape { protected DrawAPI drawAPI; //动态传入,建立委派关系 protected Shape(DrawAPI drawAPI) { this.drawAPI = drawAPI; } public abstract void draw(); }

    实现Shape 接口的具体类

    public class Circle extends Shape { private int x, y, radius; public Circle(int x, int y, int radius, DrawAPI drawAPI) { super(drawAPI); this.x = x; this.y = y; this.radius = radius; } public void draw() { drawAPI.drawCircle(radius,x,y); } }

    在客户端使用Shape和DrawAPI类绘制不同的彩色Circle,将DrawRedCircle桥接到Shape类中

    public class BridgePatternDemo { public static void main(String[] args) { Shape redCircle = new Circle(100,100, 10, new DrawRedCircle()); Shape greenCircle = new Circle(100,100, 10, new DrawGreenCircle()); redCircle.draw(); greenCircle.draw(); } }

    代理模式

    某个对象比较“敏感”/“私密”/“贵重”,不希望被client直接访问 到,故设置proxy,在二者之间建立防火墙。 案例:

    public interface Image { void display(); }

    真正的类,其中的方法存在每次创建都要 从磁盘装载, 代价高的问题

    public class RealImage implements Image { private String fileName; public RealImage(String fileName) { this.fileName = fileName; loadFromDisk(fileName); } @Override public void display() {} private void loadFromDisk(String fileName) {} }

    代理类,通过委派到原来的类来完成具体装载 相较于真实的类,代理的类不需要在构造的时候从文件装载,只要在display 的时候发现没有装载,则再 委派到真实的类去装载。

    public class ProxyImage implements Image { private Image realImage; private String fileName; public ProxyImage(String fileName) { this.fileName = fileName; } @Override public void display() { if(realImage == null) { //委派 realImage = new RealImage(fileName); } realImage.display(); } }

    和适配器模式之间的区别: 代理模式的目的是隔离对复杂 对象的访问,降低难度/代价,定位在“访问/使用行为 而适配器模式的目的是消除不兼容,让B以客户端期望的统一的方式与A建立起联系。

    三、行为模式

    Observer模式

    可以形象的比喻为“粉丝”对“偶像”感兴趣,希望随时得知偶像的一举一动 粉丝到偶像那里注册,偶像一旦有新闻发生,就推送给已注册的粉丝 (回调callback粉丝的特定功能) 案例代码: 明星类:observers中维持一组“对自己感兴趣的”对象 通过attach(observer)方法允许“粉丝”调用该方法向自己注册,将 其加入队列 在一旦有新闻发生(状态变化),通知所有“粉丝” 通过notifyAllObservers()方法调用“粉丝” 的update操作,向粉丝“广播”自己的变化

    public class Subject { private List<Observer> observers = new ArrayList<Observer>(); private int state; public int getState() { return state; } public void setState(int state) { this.state = state; notifyAllObservers(); } public void attach(Observer observer) { observers.add(observer); } private void notifyAllObservers() { for (Observer observer : observers) { observer.update(); } } }

    “粉丝” 的抽象接口

    public abstract class Observer { protected Subject subject; public abstract void update(); }

    具体的粉丝类,在构造时,指定自己的“偶像”subject, 通过调用attach()方法把自己注册给它 当“偶像”有状态变化 时,偶像会用notifyAllObservers()方法调用“粉丝” 的update()操作,粉丝再使用 subject.getState() 获取偶像最新信息

    public class BinaryObserver extends Observer { public BinaryObserver(Subject subject) { this.subject = subject; this.subject.attach(this); } @Override public void update() { System.out.println( "Binary String: " + Integer.toBinaryString( subject.getState() ) ); } }

    优点:

    主体与观察者之间的耦合度低:主体不了解家属主体可以动态增加和删除观察者主题不需要也不能控制观察者

    Visitor 访客模式

    对特定类型的object的特定操作(visit),在运行时将 二者动态绑定到一起,该操作可以灵活更改,无需更改被visit的类 。而将数据和作用于数据上的某种/些特定操作分离开来。 这就需要为ADT预留一个将来可扩展功能的“接入点”,外部实现的功能代码 可以在不改变ADT本身的情况下通过委派的方式接入ADT

    案例: 其中accept()方法将处理数据的功能委派到外部传入的 visitor

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

    visit的实现:

    /* 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; } }

    客户端代码 在calculatePrice方法中,只要更换 visitor的具体实现,即可切换算法

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

    visit模式在特定ADT上执行某种特定操作,但该操作不在ADT内部实现,而是委派到独立的visitor对象,客户端可灵活 扩展/改变visitor的操作算法,而不影响ADT 而迭代器模式是以遍历的方式访问集合数据而无需暴露其内部表示,将“遍历”这项功能委派到外部的iterator对象。

    Processed: 0.010, SQL: 10