设计模式 Design Patterns

岳浩
2023-12-01

设计模式 Design Patterns

设计模式的基础:抽象,封装,多态,继承

良好的OO设计必须具备:可复用、可扩充、可维护三个特性

OO原则

  1. 合成/聚合复用原则:封装变化,多用组合,减少继承
  2. 针对接口编程,不针对实现编程
  3. 为交互对象之间的松耦合设计而努力
  4. 开放封闭原则:对扩展开放,对修改关闭
  5. 依赖倒置原则:依赖抽象不要依赖具体类
  6. 最少知识原则:一个对象应该对其他对象保持最少的了解
  7. 好莱坞原则:Don‘ t call us, we’ ll call you
  8. 单一责任原则:一个类应该只有一个引起变化的原因

类之间的关系:IS-A, HAS-A, IMPLEMENTS

 
 

策略模式 Strategy Pattern

定义

​ 定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
 

代码示例

public abstract class Duck{
    FlyBehavior flyBehavior;
    QuackBehavior quackBehavior;
    public Duck(){}
    pulbic void setFlyBehavior(FlyBehavior fb){flyBehavior = fb;}
    pulbic void setQuackBehavior(QuackBehavior qb){quackBehavior = qb;}
    public abstract void display();
    public void performFly(){
        flyBehavior.fly();
    }
    public void performQuack(){
        quackBehavior.quack();
    }
}

​ 鸭子所做的一组行为,即一族算法,可以利用set方法被替换,这种方法就是组合,将两个类结合起来使用,使用组合建立系统具有很大的弹性,可以在运行时动态地改变行为。

主要解决问题

​ 解决算法行为类似时,过多if…else…问题。

解决途径

​ 共同实现一个接口,将算法封装成类,需要时进行替换。
 
 

观察者模式 Observer Pattern

定义

​ 在对象之间定义了一对多的依赖,这样依赖,当一个对象改变状态,依赖他的对象都会收到通知并自动更新。

代码示例

public class WeatherData extends Observable{
    private float temperature;
    private float pressure;
    public void measurementsChanged(){
        setChanged();
        notifyObservers();
    }
    public void setMeasurements(float temperature,float pressure){
        this.temperature = temperature;
        this.pressure = pressure;
        measurementsChanged();
    }
    public float getTemperature(){...};
    public float getPressure(){...};
}

public class ConditionDisplay implements Observer{
    Observable observable;
    private float temperature;
    private float pressure;
    public ConditionDisplay(Observable ob){
        this.observable = ob;
        observable.addObserver(this);
    }
    public void update(Observable obs, Object arg){
        if(obs instanceof WeatherData){
            WeatherData wd = (WeatherData) obs;
            this.temperature = wd.getTemperature();
            this.pressure = wd.getPressure();
            display();
        }
    }
    public void display(){
        print(...);
    }
}

​ 利用JDK内置支持,首先”主题“继承Observable接口,“观察者”实现Observer接口。在Observable类中,先调用setChange()方法,标记状态改变,在调用notifyObservers()方法,方法中会调用它的观察者的update()方法,其中参数Object arg可决定”推“或”拉“数据。

主要解决问题

​ 一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。

解决途径

​ 实现Observable和Observer接口,通过遍历注册的观察者对象数组,广播通知。
 
 

装饰者模式 Decorator Pattern

定义

​ 动态地将责任附加到对象上,若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

代码示例

public abstrack class Beverage{
    String description = "Unknown Beverage";
    public String get Description(){
        return description;
    }
    public abstract double cost();
}

pulbic abstract class CondimentDecorator extends Beverage{
    public abstract String get Description();
}
public class Espresso extends Beverage{
    public Espresso(){
        description = "Espresso";
    }
    public double cost(){
        return 1.99;
    }
}

public class HouseBlend extends Beverage{
    public HouseBlend(){
        description = "HouseBlend";
    }
    public double cost(){
        return 0.89;
    }
}
public class Mocha extends CondimentDecorator{
    Beverage beverage;
    public Mocha(Beverage beverage){
        this.beverage = beverage;
    }
    pulbic String getDescription(){
        return beverage.getDescription() + ", Mocha";
    }
    public double cost(){
        return 0.2 + beverage.cost();
    }
}
puvlic class CoffeeHouse{
    public static void main(String args[]){
        Beverage beverage = new Espresso();
        Beverage decoratedBeverage = new Mocha(beverage);
        print(decoratedBeverage.getDescription() + "$" +decoratedBeverage.cost());
    }
}

​ 保持类方法签名完整性的前提下,利用继承提供了额外的功能。我们需要有抽象组件(Beverage),具体组件(Espresso,HouseBlend),抽象装饰者(CondimentDecorator)以及具体修饰者(Mocha)。继承或实现接口的目的不是为了获得行为而是获得相同的类型。

主要解决问题

​ 防止单纯继承导致的子类膨胀问题。

解决途径

​ 通过继承和组合扩展父类。实现了对扩展开放对修改关闭。
 
 

工厂模式 Factory Pattern

定义

工厂模式是Java中最常用的设计模式之一

​ 简单工厂:不是工厂模式,只是OO编程习惯。

​ 工厂方法模式: 定义了一个创建对象的接口,但由于子类决定要求实例化的类时哪一个。工厂方法让类把实例化推迟到子类。

​ 抽象工厂模式: 提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。

代码示例

简单工厂

public class SimplePizzaFactory{
    public Pizza createPizza(String type){
        Pizza pizza = nul;
        if(type.equals("cheese")){
            pizza = new CheesePizza();
        }else if(type.equals("pepperoni")){
            pizza = new PepperoniPizza();
        }
        return pizza;
    }
}

public class PizzaStore{
    SimplePizzaFactory factory;
    public PizzaStore(SimplePizzaFactory factory){
        this.factory = factory;
    }
    public Pizza orderPizza(String type){
        Pizza pizza;
        pizza = factory.createPizza(type);
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
}

​ 简单工厂利用组合,通过接收的参数不同返回不同的对象负责具体类的实例化,但是又缺点拓展性差,不符合开闭原则,不修改代码就无法拓展。

工厂方法模式

public abstract class PizzaStore{
    public Pizza orderPizza(String type){
        Pizza pizza;
        pizza = createPizza(type);
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
    protected abstract Pizza createPizza(String type);
}

public class NyPizzaStore extends PizzaStore{
    Pizza createPizza(String item){
        if(item.equals("cheese")){
            return new NYStyleCheesePizza();
        }else if(item.equals("pepperoni")){
            return new NYStylePepperoniPizza();
        }else return null;
    }
}

​ 工厂方法模式中,PizzaStore称为创建者类,其将实例化责任转移到一个方法中即createPizza(String),这样可以将处理对象的行为交给子类负责,超类代码和子类对象常见代码解耦,同时拓展性增强,支持增加新工厂产品。

抽象工厂模式

public interface PizzaIngredientFactory{
    public Dough createDough();
    public Cheese createCheese();
}

public class NYPizzaIngredientFactory implements PizzaIngredientFactory{
    public Dough createDough(){
        return new ThinCrustDough();
    }
    public Cheese createCheese(){
        return new ReggianoCheese();
    }
}

public abstract class Pizza{
    String name;
    Dough dough;
    Cheese cheese;
    abstract void prepare();
    void bake(){
        print("bake")
    }
    //cut..box
}

public class CheesPizza extends Pizza{
    PizzaIngredientFactory ingredientFactory;
    public CheesPizza(PizzaIngredientFactory ingredientFactory){
        this.ingredientFactory = ingredientFactory;
    }
    void prepare(){
        print("preparing");
        dough = ingredientFactory.createDough();
        cheese = ingredientFactory.createCheese();
    }
}

​ 引入抽象工厂,工厂方法使用类,而抽象工厂使用对象,通过对象的组合创建一个产品家族。抽象工厂定义了一个负责创建一族产品的接口,接口中每个方法都创建一个具体产品,并用子类实现具体的做法。因此抽象工厂也是利用工厂方法的思想实现的。

区分工厂方法和抽象工厂

工厂方法抽象工厂
实现原理利用继承利用对象组合
扩展能力支持增加新产品支持增加产品族

主要解决问题

​ 封装对象的创建。

解决途径

​ 将类的实例化延迟到子类中进行。
 
 

单例模式 Singleton Pattern

定义

​ 确保一个类只有一个实例,并提供全局访问点。

代码示例

经典实现

public class Singleton{
    private static Singleton uniqueInstance;
    private Singleton(){}
    public static Singleton getInstance(){
        if(uniqueInstance == null){
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }
}

​ 用静态便令记录唯一一个实例,构造器声明为私有,禁止外部类调用构造器。

双重检查锁处理多线程

public class Singleton{
    private volatile static Singleton uniqueInstance;
    private Singleton(){}
    public static Singleton getInstance(){
        if(uniqueInstance == null){
             synchronized(Singleton.class){
                if(uniqueInstance == null){
                    uniqueInstance = new Singleton();
                }
            }   
        }
        return uniqueInstance;
    }
}

主要解决问题

​ 保证一个类仅有一个实例。

解决途径

​ 判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
 
 

命令模式 Command Pattern

定义

​ 将请求封装成对象,这可以让你使用不同的请求,队列,或者日志请求来参数化其他对象。命令模式也可以支持撤销操作。

代码示例

public interface Command{
    public void execute();
    public void undo();
}

public class LightOnCommand implements Command{
    Light light;
    public LightOnCommand(Light light){
        this.light = light;
    }
    pulbic void execute(){
        light.on();
    }
    public void undo(){
        light.off();
    }
}

public class LightOffCommand implements Command{
    Light light;
    public LightOffCommand(Light light){
        this.light = light;
    }
    pulbic void execute(){
        light.off();
    }
    public void undo(){
        light.on();
    }
}

​ 利用preCommand变量记录前一个状态即可实现undo操作,多次撤销用栈记录即可,还可以设置宏命令。

主要解决问题

​ 在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。

解决途径

​ 通过调用者调用接受者执行命令,顺序:调用者→命令→接受者。命令对象封装了接收者的一个或一组动作。
 
 

适配器模式与外观模式 Adapter Pattern & Facade Pattern

定义

​ 适配器模式:将一个类的接口,转换成客户期望的另一个接口。适配器让原本不兼容的类可以合作无间。

​ 外观模式:提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。

代码示例

适配器模式

public interface MediaPlayer {
   public void play(String audioType, String fileName);
}

public interface AdvancedMediaPlayer { 
   public void playVlc(String fileName);
   public void playMp4(String fileName);
}

public class VlcPlayer implements AdvancedMediaPlayer{
   @Override
   public void playVlc(String fileName) {
      System.out.println("Playing vlc file. Name: "+ fileName);      
   }

   @Override
   public void playMp4(String fileName) {
      //什么也不做
   }
}

public class Mp4Player implements AdvancedMediaPlayer{
   @Override
   public void playVlc(String fileName) {
      //什么也不做
   }

   @Override
   public void playMp4(String fileName) {
      System.out.println("Playing mp4 file. Name: "+ fileName);      
   }
}

public class MediaAdapter implements MediaPlayer {
   AdvancedMediaPlayer advancedMusicPlayer;
   public MediaAdapter(String audioType){
      if(audioType.equalsIgnoreCase("vlc") ){
         advancedMusicPlayer = new VlcPlayer();       
      } else if (audioType.equalsIgnoreCase("mp4")){
         advancedMusicPlayer = new Mp4Player();
      }  
   }

   @Override
   public void play(String audioType, String fileName) {
      if(audioType.equalsIgnoreCase("vlc")){
         advancedMusicPlayer.playVlc(fileName);
      }else if(audioType.equalsIgnoreCase("mp4")){
         advancedMusicPlayer.playMp4(fileName);
      }
   }
}

public class AudioPlayer implements MediaPlayer {
   MediaAdapter mediaAdapter; 
   @Override
   public void play(String audioType, String fileName) {    
      //播放 mp3 音乐文件的内置支持
      if(audioType.equalsIgnoreCase("mp3")){
         System.out.println("Playing mp3 file. Name: "+ fileName);         
      } 
      //mediaAdapter 提供了播放其他文件格式的支持
      else if(audioType.equalsIgnoreCase("vlc") 
         || audioType.equalsIgnoreCase("mp4")){
         mediaAdapter = new MediaAdapter(audioType);
         mediaAdapter.play(audioType, fileName);
      }
      else{
         System.out.println("Invalid media. "+
            audioType + " format not supported");
      }
   }   
}

public class AdapterPatternDemo {
   public static void main(String[] args) {
      AudioPlayer audioPlayer = new AudioPlayer();
      audioPlayer.play("mp3", "beyond the horizon.mp3");
      audioPlayer.play("mp4", "alone.mp4");
      audioPlayer.play("vlc", "far far away.vlc");
      audioPlayer.play("avi", "mind me.avi");
   }
}

 
 

外观模式

public interface Shape {
   void draw();
}

public class Rectangle implements Shape {

   @Override
   public void draw() {
      System.out.println("Rectangle::draw()");
   }
}

public class Square implements Shape {

   @Override
   public void draw() {
      System.out.println("Square::draw()");
   }
}

public class Circle implements Shape {

   @Override
   public void draw() {
      System.out.println("Circle::draw()");
   }
}

public class ShapeMaker {
   private Shape circle;
   private Shape rectangle;
   private Shape square;

   public ShapeMaker() {
      circle = new Circle();
      rectangle = new Rectangle();
      square = new Square();
   }
   public void drawCircle(){
      circle.draw();
   }
   public void drawRectangle(){
      rectangle.draw();
   }
   public void drawSquare(){
      square.draw();
   }
}

public class FacadePatternDemo {
   public static void main(String[] args) {
      ShapeMaker shapeMaker = new ShapeMaker();
      shapeMaker.drawCircle();
      shapeMaker.drawRectangle();
      shapeMaker.drawSquare();      
   }
}

主要解决问题

​ 适配器:需要使用一个现有类但是其接口不符合需求时。

​ 外观:需要简化并统一一个很大的接口时

解决途径

​ 适配器:Adapter实现target接口

​ 外观:统一简化接口
 
 

模板方法模式 Template Pattern

定义

​ 在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

代码示例

public abstract class CaffeineBeverage{
    public final void prepareRecipe(){
        boilWater();
        brew();
        pourInCup();
        addCondiments();//hook
    }
    abstract void brew();
    void addCondiments(){
        print("add noting.");
    };
    void boilWater(){
        print("...");
    }
    void pourInCup(){
        print("...");
    }
}

public class Coffee extends CaffeineBeverage{
    public void brew(){
        print("Dripping Coffee through filter");
    }

    @Override
    pulbic void addCondiments(){
        print("add milk");
    }
}

​ 当子类必须提供算法的某个实现时就使用抽象方法,并且可以设置hook,钩子在抽象类中不做事或者只做默认的事情,子类可以选择实现钩子但是不强制。算法中个别步骤可以又不同的实现但是算法结构是不变的,为了防止改变模板,可以将模板方法声明为final。区分策略模式和模板方法模式,一个用组合一个用继承。工厂方法是模板方法的一个特殊版本。

主要解决问题

​ 一些方法通用,却在每一个子类都重新写了这一方法。

解决途径

​ 将通用算法抽象出来,具体操作可以转移到子类实现,封装不变的部分,扩展可变部分。
 
 

迭代器模式和组合模式 Iterator Pattern&Composite Pattern

定义

​ 迭代器模式:提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。

​ 组合模式:允许你将对象组合树形结构来表现“整体/部分”的层次结构。组合能让客户以一致的方法处理个别对象和对象组合。

代码示例

迭代器模式

public class BreakFastIterator implements Iterator {
    MenuItem[] items;
    int position = 0;
    public BreakFastIterator(MenuItem[] items) {
        this.items = items;
    }
    @Override
    public boolean hasNext() {
        if(position >= items.length || items[position] == null){
            return false;
        }else{
            return true;
        }
    }
    @Override
    public Object next() {
        MenuItem item = items[position];
        position += 1;
        return item;
    }
    @Override
    public void remove() {
        Iterator.super.remove();
    }
    @Override
    public void forEachRemaining(Consumer action) {
        Iterator.super.forEachRemaining(action);
    }
}

public class BreakFastMenu implements Menu {
    static final int MAX_ITEMS = 10;
    MenuItem[] menuItems;
    int numOfMenu = 0;
    public BreakFastMenu(){
        menuItems = new MenuItem[MAX_ITEMS];
        addMenuItem("鸡蛋","1");
        addMenuItem("蛋糕","5");
    }
    @Override
    public void addMenuItem(String name,String price){
        MenuItem item = new MenuItem(name,price);
        if(numOfMenu > 10){
            System.out.println("不加了");
        }else menuItems[numOfMenu++] = item;
    }
    @Override
    public Iterator createIterator() {
        return new BreakFastIterator(menuItems);
    }
}

public class Waiter {
    Menu BreakFastMenu;
    Menu DinnerMenu;
    public Waiter(Menu breakFastMenu, Menu dinnerMenu) {
        this.BreakFastMenu = breakFastMenu;
        this.DinnerMenu = dinnerMenu;
    }
    public void printMenu(){
        Iterator breakfastIterator = BreakFastMenu.createIterator();
        Iterator dinnerIterator = DinnerMenu.createIterator();
        System.out.println("------breakfast---------");
        printMenu(breakfastIterator);
        System.out.println("------dinner-------------");
        printMenu(dinnerIterator);
    }
    private void printMenu(Iterator iterator) {
        while(iterator.hasNext()){
            MenuItem item = (MenuItem) iterator.next();
            System.out.println(item.name + "  $" + item.price);
        }
    }
}

​ 这里用了jdk自带迭代器接口,思路类似与抽象工厂,把元素之间游走的责任交给迭代器,而不是聚合对象。Java5之后支持for(Object obj : collection)语句不用显式地创建迭代器对象。
 
 

组合模式

public class Employee {
   private String name;
   private String dept;
   private int salary;
   private List<Employee> subordinates;
   public Employee(String name,String dept, int sal) {
      this.name = name;
      this.dept = dept;
      this.salary = sal;
      subordinates = new ArrayList<Employee>();
   }
   public void add(Employee e) {
      subordinates.add(e);
   }
   public void remove(Employee e) {
      subordinates.remove(e);
   }
   public List<Employee> getSubordinates(){
     return subordinates;
   }
   public String toString(){
      return ("Employee :[ Name : "+ name 
      +", dept : "+ dept + ", salary :"
      + salary+" ]");
   }   
}


public class CompositePatternDemo {
   public static void main(String[] args) {
      Employee CEO = new Employee("John","CEO", 30000);
      Employee headSales = new Employee("Robert","Head Sales", 20000);
      Employee headMarketing = new Employee("Michel","Head Marketing", 20000);
      Employee salesExecutive1 = new Employee("Richard","Sales", 10000);
      Employee salesExecutive2 = new Employee("Rob","Sales", 10000);
      CEO.add(headSales);
      CEO.add(headMarketing);
      headSales.add(salesExecutive1);
      headSales.add(salesExecutive2);
      System.out.println(CEO); 
      for (Employee headEmployee : CEO.getSubordinates()) {
         System.out.println(headEmployee);
         for (Employee employee : headEmployee.getSubordinates()) {
            System.out.println(employee);
         }
      }        
   }
}

​ 组合模式与迭代器模式共同使用比较复杂,组合模式建立了任意复杂的树状图,迭代器提供通用接口,可以做到遍历组件内所有菜单项。在调用过程中,叶子节点可以使用空迭代器,它的hasNext()永远返回false。

主要解决问题

​ 迭代器模式:不同的方式来遍历整个整合对象。

​ 组合模式:它在我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。

解决途径

​ 迭代器模式:实现Iterator接口。

​ 组合模式:树枝和叶子实现统一接口,树枝内部组合该接口。
 
 

状态模式 State Pattern

定义

​ 允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。

代码示例

public interface State {
   public void start(Context context);
   public void stop(Context context);
}

public class StartState implements State {
   Context context;
   public StartState(Context context){
      this.context = context;
   }
   public void start() {
      print("you are in game.");
   }
   public void stop() {
      System.out.println("stop the game.");
      context.setState(context.getStopState()); 
   }
   public String toString(){
      return "Start State";
   }
}

public class StopState implements State {
   Context context;
   public StopState(Context context){
      this.context = context;
   }
   public void start() {
      System.out.println("start the game.");
      context.setState(context.getStartState());
   }
   public void stop() {
      System.out.println("you already stop the game.");
   }
   public String toString(){
      return "Stop State";
   }
}

public class Context {
   private State startState;
   private State stopState;
   private State state = stopState;
   public Context(){
      startState = new startState(this);
      stopState = new StopState(this);
   }
   public void start(){
      state.start();
   }
   public void stop(){
      state.stop();
   }
   public void setState(State state){
      this.state = state;     
   }
   public State getxxState(){//get每一种状态
      return xxstate;
   }
    ......
}

public class StatePatternDemo {
   public static void main(String[] args) {
      Context context = new Context();
      context.start();
      contex.stop();
   }
}

​ 将状态封装成类,虽然会造成类的增多但是暴露在外的类只有Context上下文,类似于策略模式,但是策略模式是被动改变行为,状态模式是随时间变化而主动在建立好的方案中改变行为,类图和策略模式一致但是具体实现和目的不同。

主要解决问题

​ 对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。

解决途径

​ 将具体状态抽象成类,实现一个共同的State接口,在状态类内执行相关行为,执行过后按照设计改变状态。
 
 

代理模式 Proxy Pattern

定义

​ 为另一个对象提供一个替身或占位符以访问这个对象。

代码示例

RMI远程代理

public interface State extends Serializable {
    void start();
    void stop();
}

public interface GameStationRemote extends Remote {
    String getType() throws RemoteException;
    String getLocation() throws RemoteException;
    State getState() throws RemoteException;
    void start() throws RemoteException;
    void stop() throws RemoteException;
}

public class GameStation extends UnicastRemoteObject implements GameStationRemote {
    private StartState startState;
    private StopState stopState;
    private String Type;
    private String Location;
    public State state = null;
    public GameStation(String Type, String Location) throws RemoteException {
        startState = new StartState(this);
        stopState = new StopState(this);
        this.Type = Type;
        this.Location = Location;
        state = stopState;
    }
    public void start(){state.start();}
    public void stop(){state.stop();}
    public String getLocation(){return Location;}
    public String getType() {return Type;}
    public StartState getStartState() {return startState;}
    public StopState getStopState() {return stopState;}
    public State getState() {return state;}
    public void setState(State state) {this.state = state;}

public class GameStationMonitor {
    GameStationRemote machine;
    public GameStationMonitor(GameStationRemote machine){this.machine = machine;}
    public void report() throws RemoteException {
        System.out.println("-------------------------------------");
        System.out.println("Type: "+ machine.getType());
        System.out.println("Location "+ machine.getLocation());
        System.out.println("State: "+ machine.getState());
        System.out.println("-------------------------------------");
    }
}

public class GameStationTest {
    public static void main(String[] args) throws RemoteException, MalformedURLException {
        LocateRegistry.createRegistry(1099);
        GameStation gameStation = new GameStation("MARK1","HEULibrary");
        Naming.rebind("MARK1/GameStation1",gameStation);
    }
}

public class test {
    public static void main(String[] args) throws RemoteException {
        GameStationMonitor monitor = null;
        GameStationRemote machine = null;
        try{
            machine = (GameStationRemote)
                    Naming.lookup("rmi://127.0.0.1:1099/MARK1/GameStation1");
            monitor = new GameStationMonitor(machine);
        }catch (Exception e){
            e.printStackTrace();
        }
        monitor.report();
        machine.start();
        monitor.report();
    }
}

​ 远程代理可以由本地方法调用的对象,其行为转发到远程对象中。利用RMI,客户对象调用stub的方法,stub的方法通过网络Socket输出流运给skeleton然后找到服务对象上的真正的方法,返回结果过程相同。为实现RMI,首先需要制作远程接口扩展Remote,stub和实际的服务都实现此接口。rmic帮助产生stub和skeleton,然后注册服务,客户就可调用远程接口方法了。

虚拟代理

public interface ImgIcon extends Icon {
    ImageIcon setScale(int width);
}

public class ImageProxy implements ImgIcon{
    ImageIcon imageIcon = null;
    URL imageURL;
    Thread retrievalThread;
    boolean retrieval = false;
    final int width = 480;
    double scale = 1.0;
    public ImageProxy(URL url) {this.imageURL = url;}
    @Override
    public ImageIcon setScale(int width) {
        Image image = imageIcon.getImage();
        scale = width * 1.0 / imageIcon.getIconWidth();
        image = image.getScaledInstance(width, (int)(imageIcon.getIconHeight()*scale),Image.SCALE_SMOOTH);
        imageIcon.setImage(image);
        return imageIcon;
    }
    @Override
    public void paintIcon(Component c, Graphics g, int x, int y) {
        if(imageIcon != null){
            imageIcon.paintIcon(c,g,x,y);
        }else{
            g.drawString("loading.....",200,300);
            if(retrieval == false){
                retrieval = true;
                retrievalThread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try{
                            imageIcon = new ImageIcon(imageURL,"CD cover");
                            imageIcon = setScale(width);
                            c.repaint();
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                });
                retrievalThread.start();
            }
        }
    }
    @Override
    public int getIconWidth() {
        if(imageIcon == null){
            return 800;
        }else{
            return imageIcon.getIconWidth();
        }
    }
    @Override
    public int getIconHeight() {
        if(imageIcon == null){
            return 600;
        }else{
            return imageIcon.getIconHeight();
        }
    }
}

public class ImageComponent extends JComponent {
    ImgIcon icon;
    @Override
    public void paint(Graphics g) {
        super.paint(g);
        if(icon == null){
            g.drawString("请选择",50,100);
        }else{
            int iconWidth = icon.getIconWidth();
            int iconHeight = icon.getIconHeight();
            int x = (this.getWidth() - iconWidth) / 2;
            int y = (this.getHeight() - iconHeight) / 2;
            icon.paintIcon(this,g,x,y);
        }
    }
    public void setIcon(ImgIcon icon){
        this.icon = icon;
    }
}

public class cdFrame extends JFrame {
    public static final int width = 800;
    public static final int height = 600;

    public JFrame frame;
    public JMenuBar menuBar;
    public JMenu menu;
    public JMenuItem item;
    public ImgIcon icon;
    public ImageComponent imageComponent = new ImageComponent();
    Map<String, URL> cdList;
    public cdFrame(Map<String,URL> cdList){
        this.cdList = cdList;
        frame = new JFrame("CD Cover");
        frame.setSize(width,height);
        frame.setLayout(new BorderLayout());
        frame.setResizable(false);
        menuBar = new JMenuBar();
        menu = new JMenu("My Favorite");
        frame.setJMenuBar(menuBar);
        menuBar.add(menu);
        for(String cdName : cdList.keySet()){
            item = new JMenuItem(cdName);
            menu.add(item);
            URL imgURL = cdList.get(cdName);
            item.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    icon = new ImageProxy(imgURL);
                    imageComponent.setIcon(icon);
                    frame.repaint();
                }
            });
        }
        frame.add(imageComponent,BorderLayout.CENTER);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}

public class main {
    public static Map<String,URL> cdList;
    public static void main(String[] args) throws MalformedURLException {
        cdList = new HashMap<>();
        cdList.put("cd1",new URL("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fi0.hdslb.com%2Fbfs%2Falbum%2Ff713f3c2ccfe83b2f646e64332ac22717e59bf1b.png&refer=http%3A%2F%2Fi0.hdslb.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1638235277&t=5043696bf3a55fa600f7411892062b7f"));
        cdList.put("cd2",new URL("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic1.win4000.com%2Fwallpaper%2F8%2F57eb31eb0aca9.jpg%3Fdown&refer=http%3A%2F%2Fpic1.win4000.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1638235277&t=d5620eb449662be7daec8f574888a963"));
        cdFrame frame = new cdFrame(cdList);
    }
}

​ 代理的目的是控制对象的访问,一般情况下会用工厂返回实例化的代理对象,因此客户是不知道使用的是代理还是真正的对象。虚拟代理控制访问实例化开销大的对象。结构上类似装饰者模式,但是目的不同,装饰者模式为对象加上行为,而代理控制访问。

动态代理

public interface PersonBean {
    String getName();
    String getGender();
    String getInterests();
    int getHotOrNotRating();

    void setName(String name);
    void setGender(String gender);
    void setInterests(String interests);
    void setHotOrNotRating(int rating);
}

public class PersonBeanImpl implements PersonBean {
    private String name;
    private String gender;
    private String interests;
    private int rating;
    private int ratingCount = 0;
    @Override
    public String getName() {
        return name;
    }
    @Override
    public String getGender() {
        return gender;
    }
    @Override
    public String getInterests() {
        return interests;
    }
    @Override
    public int getHotOrNotRating() {
        if(ratingCount == 0) return 0;
        else return rating / ratingCount;
    }
    @Override
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public void setGender(String gender) {
        this.gender = gender;
    }
    @Override
    public void setInterests(String interests) {
        this.interests = interests;
    }
    @Override
    public void setHotOrNotRating(int rating) {
        this.rating += rating;
        ratingCount++;
    }
}

public class OwnerInvocationHandler implements InvocationHandler {
    PersonBean person;
    public OwnerInvocationHandler(PersonBean person) {
        this.person = person;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(method.getName().startsWith("get")){
            return method.invoke(person,args);
        }else if(method.getName().equals("setHotOrNotRating")){
            throw new IllegalAccessException();
        }else if(method.getName().startsWith("set")){
            return method.invoke(person,args);
        }
        return null;
    }
}

public class nonOwnerInvocationHandler implements InvocationHandler {
    PersonBean person;
    public nonOwnerInvocationHandler(PersonBean person) {
        this.person = person;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(method.getName().startsWith("get")){
            return method.invoke(person,args);
        }else if(method.getName().equals("setHotOrNotRating")){
            return method.invoke(person,args);
        }else if(method.getName().startsWith("set")){
            throw new IllegalAccessException();
        }
        return null;
    }
}

public class Test {
    PersonBean ma;
    public static void main(String[] args) {
        Test test = new Test();
        test.init();
        test.drive();
    }
    private void init() {
        ma = new PersonBeanImpl();
        ma.setName("maRuncheng");
        ma.setGender("man");
        ma.setInterests("java");
        ma.setHotOrNotRating(8);
    }
    public void drive(){
        PersonBean ownerProxy = getOwnerProxy(ma);
        System.out.println("Name is " + ownerProxy.getName());
        ownerProxy.setInterests("c++");
        System.out.println("change he mind to " + ownerProxy.getInterests());
        try{
            ownerProxy.setHotOrNotRating(10);
        }catch (Exception e){
            System.out.println("you can't rating yourself");
        }
        System.out.println("Rating is " + ownerProxy.getHotOrNotRating());
        System.out.println("========================================");
        PersonBean nonOwnerProxy = getNonOwnerProxy(ma);
        System.out.println("Name is " + ownerProxy.getName());
        try{
            nonOwnerProxy.setInterests("c++");
        }catch (Exception e){
            System.out.println("you can't change his interest");
        }
        System.out.println("ma still like " + ownerProxy.getInterests());
        nonOwnerProxy.setHotOrNotRating(10);
        System.out.println("Now,rating increase to " + ownerProxy.getHotOrNotRating());

    }
    public static PersonBean getOwnerProxy(PersonBean person){
        return (PersonBean) Proxy.newProxyInstance(person.getClass().getClassLoader(),
                person.getClass().getInterfaces(),
                new OwnerInvocationHandler(person));
    }
    public static PersonBean getNonOwnerProxy(PersonBean person){
        return (PersonBean) Proxy.newProxyInstance(person.getClass().getClassLoader(),
                person.getClass().getInterfaces(),
                new nonOwnerInvocationHandler(person));
    }
}

​ 提供InvocationHandler实现invoke方法,实际Subject的方法就由InvocationHandler控制访问。InvocationHandler是一个帮助Proxy的类,proxy将调用转发交给它处理,Proxy本身又静态方法Proxy.newProxyInstance动态创建。另外,使用jdk动态代理的Subject必须有接口。

主要解决问题

​ 控制希望访问的类。

解决途径

​ 增加代理层。
 
 

复合模式 Compound Pattern

定义

​ 复合模式结合两个或以上的模式,组成一个解决方案,解决一再发生的一般性问题。

 类似资料: