山东大学面向对象技术期末复习——设计原则和设计模式详解

面向对象技术

写在前面:本文是主要针对山东大学软件学院面向对象技术课程的设计模式和设计原则部分的总结,包括老师课上讲解的主要设计模式,类图和往年回忆考题中的设计类题目和自己写的代码(都是正确可运行的),希望能帮学弟学妹们做个总结,题主oop96,祝愿学弟学妹oop考试成绩95+

设计原则和设计模式

目标:提高面向对象设计复用性的设计原则。满足可拓展性灵活性可插入性

复用:抽象模块要尽量独立于具体层次的设计

面向对象的设计原则
  1. 开闭原则OCP: Open-Closed Principle
  2. 里氏代换原则LSP: Liskov Subtitution
  3. 依赖倒置原则DIP: Dependency Inversion Principle
  4. 接口隔离原则ISP: Interface Segregation Principle
  5. 组合复用原则CRP: Composition Reuse Principle
  6. 迪米特法则LoD: Law of Demeter
  7. 单一职责原则SRP
开闭原则OCP: Open-Closed Principle
  • 定义:软件组成实体应该是对扩展开放的(可扩展的),但是对修改是关闭的。试图设计永远不需要改变的模块(关键在于抽象层次结构的设计)
  • 目标:尝试在软件系统中减少不满足OCP原则的模块数量。同时做到解耦

题目举例

  • 给定某几种商品,要求商品可以拓展,商品要求可以设置价格,并且可能设置商品折扣
// Part表示商品类,其余商品通过继承拓展
class Part{
	protected double price; // 价格变量
	public void setPrice(double price){ 
        this.price = price;
	}
 	public double getPrice() { 
        return price;
	}
}

class Memory extends Part{}

class Disk extends Part{}

public class Test{
	public static void main(String[] args) { 
        Part p1 = new Memory(); 
        p1.setPrice(599);
		Part p2 = new Disk(); 
        p2.setPrice(499); 
        Part[] com = {p1, p2};
		System.out.println(totalPrice(com));
    }
    public static double totalPrice(Part[] parts){ 
        double total = 0.0;
		for (int i=0;i<parts.length;i++){ 
            total += parts[i].getPrice();
		}
		return total;
	}
}
//当考虑到内存折扣时,上述代码中的totalPrize方法需要修改,这违反OCP原则

因此,我们引入一个PricePolicy类,通过对其进行继承以提供不同的商品折扣

// 采用一个PricePolicy类,通过对其进行继承以提供不同的计价策略
class Part{
	protected double rice; 
    private PricePolicy pricePolicy;
	public void setPricePolicy(PricePolicy policy){ 				
		pricePolicy = policy;
	}
	public void setPrice(double price){ 
        this.price = price;
	}
	public double getPrice(){ 
        if(pricePolicy == null)
			return price;
		else
			return pricePolicy.getPrice(price);
	}
}

class Memory extends Part{}

class Disk extends Part{}

class PricePolicy{
    public double getPrice(double basePrice){
        return basePrice;
    }
}

// 当需要进行促销策略的时候,直接创建具体的实现类sale
class Sale extends PricePolicy{ 
    private double discount;
	public void setDiscount(double discount){ 
        this.discount = discount;
	}
	public double getPrice(double basePrice){ 
        return basePrice * discount;
	}
}
public class Test{    
    public static void main(String[] args){      
        Part p1 = new Memory();        
        p1.setPrice(599);        
        Part p2 = new Disk();        
        p2.setPrice(499);       
        Sale sale = new Sale();        			     
        sale.seyDiscount(0.75);      
        p2.setPricePolicy(sale);        
        Part[] com = {p1, p2};     
        System.out.println(totalPrice(com));   
    }
    public static double totalPrice(Part[] parts){ 
        double total = 0.0;
		for (int i=0;i<parts.length;i++){ 
            total += parts[i].getPrice();
		}
		return total;
	}
}
//使用这种方法,我们可以在运行时动态地设置Part对象所引用的PricePoilcy对象
  • 绘制图形,并提供展示方法,要求每增加一个新的图形,都要尽量满足OCP原则
class Shape{}
class Circle extends Shape {
    void drawCircle(){
        System.out.println("I am drawing a circle in switch!"); 
    }
}
class Square extends Shape{
    void drawSquare(){
        System.out.println("I am drawing a square in switch!"); 
    }
}

import java.util.ArrayList;
import java.util.List;
public class Test{    
    public static void main(String[] args){        
        List<Shape> shapeList=new ArrayList<Shape>();        
        Circle c=new Circle();        
        Square s=new Square();        
        shapeList.add(c);        
        shapeList.add(s);        
        switchDraw(shapeList);   
    }    
    static void switchDraw(List shapeList){        
        for(int i=0;i<shapeList.size();i++){  
            if(shapeList.get(i) instanceof Circle){ 
                Circle circle= (Circle)shapeList.get(i);                 	 
                circle.drawCircle();           
            }else if(shapeList.get(i) instanceof Square){ 
                Square square= (Square)shapeList.get(i);     
                square.drawSquare();           
            }       
        }   
    }
}
// 这种实现如果在图形形状多的情况下循环分支数量会很大,不推荐

我们编写一个shape抽象类,该类只有一个抽象方法draw(),所有的形状都从该类派生。需要增加新的形状的时候只需要增加一个新的派生类,院优代码不需要改变

import java.util.ArrayList;
abstract class shape {
    abstract void draw();
}
class circle extends shape{
    void draw(){
        System.out.println("a circle is drawing");
    }
}
class square extends shape{
    void draw(){
        System.out.println("a square is drawing");
    }
}
public class Test{
    public static void main(String[] args) {
        ArrayList<shape> arrayList = new ArrayList<shape>();
        shape circle = new circle();
        shape square = new square();
        arrayList.add(circle);
        arrayList.add(square);
        for(int i=0;i<arrayList.size();i++){
            arrayList.get(i).draw();
        }
    }
}
里氏代换原则LSP: Liskov Subtitution
  • 定义:如果对每一个类型为 T 1 T_1 T1的对象 o 1 o_1 o1,都有类型为 T 2 T_2 T2的对象 o 2 o_2 o2,使得以 T 1 T_1 T1定义的所有程序P在所有的对象 o 1 o_1 o1都换成 o 2 o_2 o2时,程序P的行为没有变化,那么类型 T 2 T_2 T2是类型 T 1 T_1 T1的子类型 (可替换原则)

  • 核心:凡是父类适用的地方子类应当也适用,继承只有满足里氏代换原则才是合理的

  • 里氏代换原则是实现开闭原则的重要方式之一,由于使用基类对象的地方都可以使用子类对象,因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象

  • 示例:鸟一般都会飞行,如燕子的飞行速度大概是每小时 120 千米。但是新西兰的几维鸟由于翅膀退化无法飞行。假如要设计一个实例,计算这两种鸟飞行 300 千米要花费的时间。显然,拿燕子来测试这段代码,结果正确,能计算出所需要的时间;但拿几维鸟来测试,结果会发生“除零异常”或是“无穷大”,明显不符合预期

    class Bird {    
        double flySpeed;   
        public void setSpeed(double speed) {       
            flySpeed = speed;   
        }    
        public double getFlyTime(double distance) {   
            return (distance / flySpeed);  
        }
    }
    //几维鸟类
    class BrownKiwi extends Bird {    
        public void setSpeed(double speed) {       
            flySpeed = 0;   
        }
    }
    public class Test {   
        public static void main(String[] args) {        
            Bird bird1 = new Swallow();        
            Bird bird2 = new BrownKiwi();   
            bird1.setSpeed(120);        
            bird2.setSpeed(120);        
            System.out.println("如果飞行300公里:");        
            try {            
                System.out.println("燕子将飞行" + bird1.getFlyTime(300) + "小时.");
                System.out.println("几维鸟将飞行" + bird2.getFlyTime(300) + "小时。");       
            } 
            catch (Exception err) {  
                System.out.println("发生错误了!");      
            }  
        }
    }
    // 这显然是不满足里氏代换原则的
    
依赖倒置原则DIP: Dependency Inversion Principle
  • 定义:高层模块不应该依赖底层模块,二者都应该依赖其抽象;抽象不应该依赖细节;而细节应该依赖抽象

  • 本质:通过抽象使得各个类或模块之间实现彼此独立,不相互影响,实现模块之间的松耦合,将具体类之间的依赖转变为抽象类或接口之间的依赖

  • 依赖倒置原则是开闭原则的基础,相对难以实现

  • 核心:针对接口编程,不要针对实现编程

  • 三种实现方式

    • 通过构造函数传递依赖对象(又称构造函数注入)
    public interface IDriver {    
        //是司机就应该会驾驶汽车    
        public void drive();
    }
    public class Driver implements IDriver {    
        private ICar car;    
        //构造函数注入    
        public Driver(ICar _car) {
            this.car = _car;   
        }    
        //司机的主要职责就是驾驶汽车通过setter方法传递依赖对象接口声明实现依赖对象    
        public void drive() {        
            this.car.run();   
        }
    }
    
    • 通过setter方法传递依赖对象
    public interface IDriver {    
        //车辆型号    
        public void setCar(ICar car);    
        //是司机就应该会驾驶汽车    
        public void drive();
    }
    public class Driver implements IDriver {    
        private ICar car;    
        public void setCar(ICar car) {       
            this.car = car;   
        }    
        //司机的主要职责就是驾驶汽车    
        public void drive() {        
            this.car.run();   
        }
    }
    public interface ICar {    
        //是汽车就应该能跑    
        public void run();
    }
    public class Benz implements ICar {   
        public void run() {        
            System.out.println("奔驰汽车开始运行...");   
        }
    }
    public class Client {    
        public static void main(String[] args) {        	
            IDriver zhangSan = new Driver();       
            ICar benz = new Benz(); //张三开奔驰车       
            zhangsan.setCar(benz);   
        }
    }
    
    • 接口声明实现依赖对象
    public interface ICar {    
        //是汽车就应该能跑    
        public void run();
    }
    public class Benz implements ICar {
        //汽车肯定会跑    
        public void run() {       
            System.out.println("奔驰汽车开始运行...");   
        }
    }
    public interface IDriver {    
        //是司机就应该会驾驶汽车    
        public void drive(ICar car);
    }
    public class Driver implements IDriver {    
        //司机的主要职责就是驾驶汽车    
        public void drive(ICar car) {       
            car.run();   
        }
    }
    public class Client {    
        public static void main(String[] args) {        
            IDriver zhangSan = new Driver();        
            ICar benz = new Benz();
            //张三开奔驰车        
            zhangSan.drive(benz);   
        }
    }
    // 优点:司机和汽车之间做到了松耦合,新汽车加入时扩展汽车类
    // 扩展汽车类时司机不需要修改
    
接口隔离原则ISP: Interface Segregation Principle
  • 定义:使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口
  • 目标:角色的合理划分,一个接口应该简单的代表一个角色(定义明确),不要把没什么联系的接口都放在一起;接口方法尽量少;一个类对于另一个类的接口依赖应该尽量少
组合复用原则CRP: Composition Reuse Principle
  • 思想:将代码易变化的部分封装起来,将其和代码不变的部分独立开来
  • 目标:实现可变性和不可变性的分离,将可变因素映射为同一个抽象类的不同子类
  • 示例:蜡笔和毛笔的区别。蜡笔的型号和颜色在出厂时就已经确定,二者不是可独立模块,但是毛笔和型号和颜色是可分离和可变化的,故可以采用桥梁模式
迪米特法则LoD: Law of Demeter
  • 要求尽量限制通信的广度和深度
  • 对接口进行分割,使其最小化,避免对客户提供不需要的服务,符合迪米特法则;体现了高内聚,低耦合
单一职责原则SRP
  • 定义:一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因;用于控制类的粒度大小

  • 问题提出:类T负责两个不同的职责:职责 P 1 P_1 、职责 P 2 P_2 。当由于职责 P 1 P_1 需求发生改变而需要修改类T时,有可能会导致原来运行的职责 P 2 P_2 功能发生故障

  • 解决方案:分别建立两个类完成对应的功能;遵循单一职责原则。分别建立两个类 T 1 T_1 T1 T 2 T_2 T2,使 T 1 T_1 T1完成职责 P 1 P_1 P1功能, T 2 T_2 T2完成职责 P 2 P_2 P2功能。这样,当修改类 T 1 T_1 T1时,不会使职责 P 2 P_2 P2发生故障风险;同理,当修改 T 2 T_2 T2时,也不会使职责 P 1 P_1 P1发生故障风险

面向对象的设计模式
设计模式的分类
  • 创建型:工厂模式,单例模式
  • 结构型:桥梁模式,适配器模式,装饰者模式
  • 行为型:观察者,责任链,策略模式
工厂模式
  • 简单工厂:一个工厂完成所有产品的生产
interface Api{
    public void operation();
}
class A implements Api{
    public void operation(){
        System.out.println("producing A");
    }
}
class B implements Api{
    public void operation(){
        System.out.println("producing B");
    }
}
class factory{
    public static Api produceApi(int condition){
        Api api = null;
        if(condition==1){
            api = new A();
        }else if(condition==2){
            api= new B();
        }
        return api;
    }
}
public class Test{
    public static void main(String[] args) {
        Api api = factory.produceApi(1);
    }
}

优点:客户端不需要修改代码,是需要传递不同的参数即可生产不同的产品,实现了客户端与具体实现类之间的解耦操作

缺点:当需要增加新的实现类的时候,不仅要增加新的实现类,而且还要修改工厂方法,而且用户知道参数代表的产品含义,暴露了一定的内部实现细节,不满足开闭原则

  • 工厂方法:不再提供一个统一的工厂类来创建所有的产品对象,而是针对不同的产品提供不同的工厂,系统提供一个与产品等级结构相对应的工厂等级结构,一个产品对应一个工厂
abstract class Product{

}
class productA extends Product{

}
abstract class Factory{
    public abstract Product factoryMethod();
}
class factoryA extends Factory{
    public Product factoryMethod(){
        Product product = new productA();
        return product;
    }
}
public class Test {
    public static void main(String[] args) {
        Factory factory = new factoryA();
        Product product;
        product = factory.factoryMethod();
    }
}

优点:具体的工厂类在实现工厂方法的时候不仅可以创建产品类对象,又可以负责产品对象的初始化以及其他的配置性工作,实现了责任分割,使得客户端代码“针对接口编程”,保持对变化的”关闭“

缺点:没有做到完全的“开闭”,对工厂方法内的修改关闭,增加新的产品需要修改工厂方法的代码

  • 抽象工厂:多个工厂用来创建一系列的产品,用以解决产品簇的问题
public interface Factory {    
	public ProductA createProductA();   
    public ProductB createProductB();
}
public interface ProductA {    
}    
public class ProductA2 implements ProductA {    
}
public class Factory1 implements Factory {    
    public ProductA createProductA() {        
        return new ProductA1();   
    }    
    public ProductB createProductB() {        
        return new ProductB1();   
    }
}
public class Factory2 implements Factory {    
    public ProductA createProductA() {       
        return new ProductA2();   
    }    
    public ProductB createProductB() {        
        return new ProductB2();   
    }
}
public class Test {    
    public static void main(String[] args) {        
        //创建抽象工厂对象         
        Factory af = new Factory1();        
        //通过抽象工厂来获取一系列的对象,如产品A和产品B          
        af.createProductA();        
        af.createProductB();   
    }
} 

抽象工厂本质上是选择产品簇的实现,而简单工厂和工厂方法侧重于对单个产品的实现和处理

当产品簇中就一个产品时,抽象工厂退化为工厂方法;当一个工厂生产所有产品时,抽象方法退化成简单工厂

单例模式
  • 定义:单例模式中确保某一个类只有一个实例,并且自行实例化和向整个系统提供该实例,单例模式多用于注册表,日志,线程池,缓存等只需要一个实例的对象
  • 实现

经典版本

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

多线程版本

public class Singleton {    
	private static Singleton uniqueInstance;      
    private Singleton() {   
    }    
    public static synchronized Singleton getInstance() {   
        if (uniqueInstance == null) {      
            uniqueInstance = new Singleton();      
        }        
        return uniqueInstance;   
    } 
}
//多线程版本只是增加了一个synochronized关键字

急切式(饥饿式)版本

public class Singleton {
    private static Singleton uniqueInstance = new Singleton();
    private Singeleton() {
        
    }
    public static Singleton getInstance() {       
        return uniqueInstance;   
    }
}
// 简单理解,无论这个单例对象是否需要,我都在该类执行时,创建该对象
桥梁模式(Bridge)
  • 设计原则:组合优先原则,在应对动态变化时优先选择组合而非继承

  • 提出的原因:继承会使得问题逐渐复杂,因为继承导致类爆炸,对于每一种情况均实现一个具体类并不可取

  • 继承的优缺点
    优点:可以容易修改或者拓展父类实现
    缺点:破坏封装性(白盒复用),继承为静态操作,不能在运行时发生改变

  • 组合的优缺点
    优点:不破坏封装(黑盒复用),动态灵活
    缺点:系统在使用委托时较为复杂

  • 示例

    • 汽车的品牌和汽车的挡位类型无关,独立变化,互不影响

      abstract class AbstractCar {
          protected Transmission gear;
          public abstract void run();
          public void setTransmission(Transmission gear) {
              this.gear = gear;
          }
      }
      class BMWCar extends AbstractCar {
          public void run() {
              gear.gear();
          }
      }
      class BenZCar extends AbstractCar {
          public void run() {
              gear.gear();
          }
      }
      abstract class Transmission {
          public abstract void gear();
      }
      class Manual extends Transmission {
          public void gear() {
              System.out.println("Manual transmission");
          }
      }
      class Auto extends Transmission {
          public void gear() {
              System.out.println("Auto transmission");
          }
      }
      public class Test {
          public static void main(String[] args) {
              Transmission manual = new Manual();
              Transmission auto = new Auto();
              AbstractCar bmw = new BMWCar();
              bmw.setTransmission(manual);
              bmw.run();
              AbstractCar benz = new BenZCar();
              benz.setTransmission(auto);
              benz.run();
          }
      }
      
    • 有大中小号三种毛笔,有红橙黄绿蓝五种颜色

      abstract class Brush {
          abstract public void print();
          abstract public void setColor(Color color);
      }
      abstract class Color {
          abstract public String getName();
      }
      class Red extends Color {
          public String name = "red";
          public String getName(){
              return name;
          }
      }
      class Orange extends Color {
          public  String name = "orange";
          public String getName(){
              return name;
          }
      }
      class Yellow extends Color {
          public  String name = "yellow";
          public String getName(){
              return name;
          }
      }
      class Green extends Color {
          public  String name = "green";
          public String getName(){
              return name;
          }
      }
      class Blue extends Color {
          public  String name = "blue";
          public String getName(){
              return name;
          }
      }
      class BigBrush extends Brush {
          public Color color;
          public String name = "bigBrush";
          public void setColor(Color color){
              this.color = color;
          }
          public void print(){
              System.out.println(name+" "+color.getName());
          }
      }
      class MiddleBrush extends Brush {
          public Color color;
          public String name = "middleBrush";
          public void setColor(Color color){
              this.color = color;
          }
          public void print(){
              System.out.println(name+" "+color.getName());
          }
      }
      class SmallBrush extends Brush {
          public Color color;
          public String name = "smallBrush";
          public void setColor(Color color){
              this.color = color;
          }
          public void print(){
              System.out.println(name+" "+color.getName());
          }
      }
      public class Test{
          public static void main(String[] args) {
              Color red = new Red();
              Color orange = new Orange();
              Color yellow = new Yellow();
              Color green = new Green();
              Color blue = new Blue();
              Brush bigBrush = new BigBrush();
              Brush middleBrush = new MiddleBrush();
              Brush smallBrush = new SmallBrush();
              bigBrush.setColor(blue);
              bigBrush.print();
              bigBrush.setColor(red);
              bigBrush.print();
          }
      }
      // 如果采用继承的话,需要有15个具体实现类,太过复杂
      ```![在这里插入图片描述](https://img-
      
      
      
适配者模式(Adapter)
  • 内容:要求对已有的代码复用,比如,现在已有“鸭子”类的情况下,我们要求增加一种“鸟”类,要求复用“鸭子”类的代码,存在新老代码接口不一致的现象,要求不能修改老代码

  • 实现方法:通过适配器

  • 示例

    • Sparrow类有方法fly使其移动,Penguin类有方法swim使其移动,不允许修改这两个类,将Sparrow和Penguin对象放入一个只能容纳一个类的列表list中,然后统一使用move方法进行移动,写出代码

      import java.util.ArrayList;
      class Sparrow{
          public void fly(){
              System.out.println("Sparrow fly");
          }
      }
      class Penguin{
          public void swim(){
              System.out.println("Penguin swim");
          }
      }
      interface Target{
          public abstract void move();
      }
      class SpTarget extends Sparrow implements Target{
          public void move(){
              super.fly();
          }
      }
      class PeTarget extends Penguin implements Target{
          public void move(){
              super.swim();
          }
      }
      public class Test{
          public static void main(String[] args) {
              Target t1 = new SpTarget();
              Target t2 = new PeTarget();
              ArrayList<Target> array = new ArrayList<Target>();
              array.add(t1);
              array.add(t2);
              for(int i=0;i<array.size();i++){
                  Target t = (Target)array.get(i);
                  t.move();
              }
          }
      }
      
    • 鸟类有两个方法——叫和飞;鸭子有两个方法——呷呷叫和短距离飞,要求不能修改老代码

      class Adaptee {    
          public void specificRequest() {     
              System.out.println("被适配类具有 特殊功能...");   
          }
      }
      interface Target {    
          public void request();
      }
      class ConcreteTarget implements Target {    
          public void request() {   
              System.out.println("普通类 具有 普通功能...");   
          }
      }
      class Adapter extends Adaptee implements Target {    
          public void request() {        
              super.specificRequest();   
          }
      }
      public class Test {   
          public static void main(String[] args) { 
              Target concreteTarget = new ConcreteTarget(); 
              concreteTarget.request(); 
              Target adapter = new Adapter();       
              adapter.request();   
          }
      }
      
装饰者模式(油漆工)
  • 定义:装饰者模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,装饰者模式动态的将责任附加到对象身上,若要拓展功能,装饰者提供了比继承更灵活的替代方案

  • 装饰必须与被装饰的组件继承自同一个接口,装饰者会将用户的请求转发给相应的组件

  • 本质:

    • 动态组合:动态地为对象增加新职责,灵活性比继承方式高
    • 在不改变原有对象的基础上,将新功能附加到对象上,拓展原有对象功能,在运行时动态决定
    • 松耦合,符合开闭原则
  • 示例

    • 假设有一个饮料店,抽象一个饮料类,如果加入不同调料的饮料都有一个名称,那么数量(种类)将会很多,对应的价格计算也会有很多结果,不方便采用继承的方式实现(当价格变动,调料增加时)

      public abstract class Beverage {    
          protected String description;   
          public String getDescription() {       
              return description;   
          }    
          public abstract double cost();
      }
      public class DarkRoast extends Beverage {   
          public DarkRoast() {        
              description = "Dark Roast Coffee";   
          }    
          public double cost() {        
              return 0.99;   
          }
      }
      public abstract class CondimentDecorator extends Beverage {    
          protected Beverage beverage;
      }
      public class Mocha extends CondimentDecorator {  
          public Mocha(Beverage beverage) {   
              this.beverage = beverage;  
          }   
          public String getDescription() {     
              return beverage.getDescription() + ", Mocha";   }    
          public double cost() {       
              return 0.20 + beverage.cost();   
          }
      }
      public class StarbuzzCoffee {   
          public static void main(String args[]) {   
              Beverage beverage = new Decaf();       
              System.out.println(beverage.getDescription() + " $" + beverage.cost());       
              Beverage beverage1 = new Decaf();      
              beverage1 = new Milk(beverage1);   
              beverage1 = new Mocha(beverage1);   
              System.out.println(beverage1.getDescription() + " $" + beverage1.cost());       
              Beverage beverage2 = new DarkRoast();    
              beverage2 = new Mocha(beverage2);    
              beverage2 = new Milk(beverage2);       
              System.out.println(beverage2.getDescription() + " $" + beverage2.cost());   
          }
      }
      
    • 假设你需要打印发票 sales ticket , 发票有抬头、正文和脚注,发票抬头可以是企事业单位,发票号等等,脚注也是一样,可能有很多不同种类的脚注需要打印。如果发票格式固定那也就没必要继续讨论了,现在的问题是,不同的客户需要的发票或者收据的抬头或脚注,他们需要的条目是不一样的,有的需要著明单位,有的只需要发票号,但是脚注需要开票人, 等等,对你来说跟现在的 Web 系统一样,客户的要求是动态;不过发票的正文是不会变化的,是固定的(和我们22考试灰常类似)

      class Invoice {
          protected Invoice ticket
          public void printInvoice() {
              System.out.println("This is the content of the invoice!");
          }
      }
      class Decorator extends Invoice {
          public Decorator(Invoice t) {
              ticket = t;
          }
          public void printInvoice() {
              if (ticket != null) {
      			ticket.printInvoice();
                  ticket.printInvoice();
              }
          }
      }
      class HeadDecorator extends Decorator {
          public HeadDecorator(Invoice t) {
              super(t);
          }
          public void printInvoice() {
              System.out.println("This is the head of the invoice!");
              ticket.printInvoice();
              ticket.printInvoice();
          }
      }
      class FootDecorator extends Decorator {
          public FootDecorator(Invoice t) {
              super(t);
          }
          public void printInvoice() {
              ticket.printInvoice();
              ticket.printInvoice();
              System.out.println("This is the foot of the invoice!");
          }
      }
      public class Test {
          public static void main(String[] args) {
              Invoice t = new Invoice();
              Invoice ticket;
              ticket = new HeadDecorator(new FootDecorator(new Decorator(t)));
              new HeadDecorator(new FootDecorator(new Decorator(t)));
              ticket.printInvoice();
              System.out.println("-----------------------");
              ticket = new HeadDecorator(new FootDecorator(new Decorator(null)));
              new HeadDecorator(new FootDecorator(new Decorator()));
              ticket.printInvoice();
          }
      }
      
观察者模式(Observer)
  • 定义:当一个对象改变时,所有订阅该对象的观察者都会收到消息

  • 本质:触发联动,当目标对象发生变化时,会联动调用这些观察者的更新方法,联动是动态的,在程序运行期间,可以通过订阅和取消订阅来动态控制观察者;实现目标对象和观察者对象的解耦

  • 适用性:一个模型的两个方面,其中有一个方面会依赖另一个方面,将二者独立封装于对象之中可以使得他们被独立的改变和复用当一个对象的改变时会引起其他对象的更新,但是不知道有多少对象需要被改变或者需要改变的对象是谁,就可以采用观察者模式解决这个问题

  • 示例:在某多人联机对战游戏中,多个玩家可以加入同一战队组成联盟,当战队中的某一成员受到敌人攻击时将给所有其他盟友发送通知,盟友收到通知后将做出响应。

    import java.util.ArrayList;
    abstract class Observer{
        public String name;
        public void setName(String name){
            this.name = name;
        }
        public abstract void beAttacked(concreteController concreteController);
        public abstract void help();
    }
    class concreteObserver extends Observer{
        public void beAttacked(concreteController concreteController){
            System.out.println(name+"被袭击了");
            concreteController.modifyAllFriends(name);
        }
        public void help(){
            System.out.println(name+"来帮你了");
        }
    }
    
    abstract class controller{
        protected ArrayList<Observer> friends = new ArrayList<Observer>();
        public void add(Observer observer){
            friends.add(observer);
        }
        public void remove(Observer observer){
            friends.remove(observer);
        }
        public abstract void modifyAllFriends(String name);
    }
    
    class concreteController extends controller{
        public void modifyAllFriends(String name){
            for(int i=0;i<friends.size();i++){
                if(friends.get(i).name!=name)
                    friends.get(i).help();
            }
        }
    }
    public class Test{
        public static void main(String[] args) {
            concreteController con = new concreteController();
            concreteObserver ob1 = new concreteObserver();
            concreteObserver ob2 = new concreteObserver();
            concreteObserver ob3 = new concreteObserver();
            ob1.setName("ob1");
            ob2.setName("ob2");
            ob3.setName("ob3");
            con.add(ob1);
            con.add(ob2);
            con.add(ob3);
    
            ob1.beAttacked(con);
        }
    }
    
    
责任链模式(Chain of Responsibility)
  • 定义:责任链模式/职责链模式属于对象的行为型模式,避免将一个请求的发送者与接收者耦合在一起,让多个对象都有机会处理请求。将接收请求的对象连接成一条链,并且沿着这条链传递请求,直到有一个对象能够处理它为止,客户端无须关心请求的处理细节以及请求的传递,只需将请求发送到链上,将请求的发送者和请求的处理者解耦

  • 适用环境:未知哪一个对象会接受请求,向多个对象中提交一个请求;动态地指定一组对象处理请求;多个对象都可处理请求,具体哪个对象会处理该请求待运行时刻才会确定

  • 优点:客户端对象无需知道是哪一个对象处理了其请求,降低了系统的耦合度;简化对象直接的相互连接;对象的职责分配更加灵活;增加一个新的具体请求处理者时无需修改原有系统的代码,仅需在客户端重新构建责任链即可

  • 缺点:不能保证请求一定会被处理,可能不被处理,对于较长的职责链,链式搜索系统性能会受到一定影响,代码调试不方便;循环调用的问题

  • 示例

    • 奖学金的层级审批,各级审批者都可以处理申请表,他们构成一条处理申请表的链式结构,申请表沿着这条链式结构传递,最常见的职责链为直线型

      public abstract class Handler {    
          //维持对下家的引用    
          protected Handler successor;   
          public void setSuccessor(Handler successor) {     
              this.successor = successor;   
          }    
          public abstract void handleRequest(String request);
      }
      // 典型的具体处理者代码
      public class ConcreteHandler extends Handler {  
          public void handleRequest(String request) {  
              if (请求满足条件) {           
                  //处理请求      
              } else {   
                  this.successor.handleRequest(request); 
                  //转发请求      
              }   
          }
      }
      // 典型的客户端代码
      public class Test {    
          public static void main(String[] args) {        
              //客户端代码:       
              Handler handler1, handler2, handler3;  
              handler1 = new ConcreteHandlerA();    
              handler2 = new ConcreteHandlerB();  
              handler3 = new ConcreteHandlerC();        
              //创建职责链 
              handler1.setSuccessor(handler2);    
              handler2.setSuccessor(handler3);
              //发送请求,请求对象通常为自定义类型  
              handler1.handleRequest("请求对象");  
          }
      }
      

    在这里插入图片描述

    class Request{
        public int number;
        public int needMoney;
        public String describe;
        public void setNeedMoney(int needMoney){
            this.needMoney = needMoney;
        }
        public void setDescribe(String describe){
            this.describe = describe;
        }
        public void setNumber(int number){
            this.number = number;
        }
    }
    abstract class Handler{
        protected Handler successor;
        protected String name;
        public Request request;
        public void setName(String name){
            this.name = name;
        }
        public void setSuccessor(Handler successor){
            this.successor = successor;
        }
        public void setRequest(Request request){
            this.request = request;
        }
        public abstract void handleRequest(Request request);
    }
    class Director extends Handler{
        public void handleRequest(Request request){
            if(request.needMoney<50000){
                System.out.println("Director:"+name+" process the request "+request.number+" "+request.describe);
            }else{
                successor.handleRequest(request);
            }
        }
    }
    class Manager extends Handler{
        public void handleRequest(Request request){
            if(request.needMoney<100000 && request.needMoney>=50000){
                System.out.println("Manager:"+name+" process the request "+request.number+" "+request.describe);
            }else{
                successor.handleRequest(request);
            }
        }
    }
    class President extends Handler{
        public void handleRequest(Request request){
            if(request.needMoney>=100000){
                System.out.println("President:"+name+" process the request "+request.number+" "+request.describe);
            }else{
                successor.handleRequest(request);
            }
        }
    }
    public class Test{
        public static void main(String[] args) {
            // 建立请求
    
            Request request = new Request();
            request.setNumber(1001);
            request.setNeedMoney(80000);
            request.setDescribe("购买倚天剑");
    
            //创建责任链中的对象
            Director director = new Director();
            director.setName("张无忌");
            Manager manager = new Manager();
            manager.setName("杨过");
            President president = new President();
            president.setName("郭靖");
    
            //创建责任链
            director.setSuccessor(manager);
            manager.setSuccessor(president);
    
            //处理请求
            director.setRequest(request);
            director.handleRequest(request);
        }
    }
    
策略模式(Strategy)
  • 定义:策略模式属于对象的行为模式

  • 目标:平滑地处理不同算法间的切换,将一组算法封装到具有共同接口的独立的类中,在相互切换(替换)的时候不影响客户端的变化

  • 模式思想:定义算法簇,分别封装起来,算法之间可以互相替换,算法和使用算法的客户之间独立变化,但是什么时候使用是客户端决定的;算法完成的是相同行为的不同实现,以相同的方式调用算法,减少算法类和使用算法类的客户之间的耦合

  • 设计原则:组合优先原则,开闭原则,封装可变性;定义策略接口,实现“针对接口编程”

  • 示例:不同的销售策略,节假日和平时的价格不同

    interface Strategy{
        public abstract double getPrize(double prize);
    }
    class normalStrategy implements Strategy{
        public double getPrize(double prize){
            return prize;
        }
    }
    class saleStrategy implements Strategy{
        public double discount = 1.0;
        saleStrategy(){
    
        }
        saleStrategy(double discount){
            this.discount = discount;
        }
        public double getPrize(double prize){
            return prize*discount;
        }
    }
    class Context{
        private Strategy strategy;
        public void setStrategy(Strategy strategy){
            this.strategy = strategy;
        }
        public double getPrize(double prize){
            return this.strategy.getPrize(prize);
        }
    }
    public class Test{
        public static void main(String[] args) {
            Context context = new Context();
            Strategy s1 = new normalStrategy();
            Strategy s2 = new saleStrategy(0.8);
            context.setStrategy(s1);
            System.out.println("平时价格");
            System.out.println(context.getPrize(200));
            context.setStrategy(s2);
            System.out.println("节日价格");
            System.out.println(context.getPrize(200));
        }
    }
    
  • 5
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沫 北

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值