设计模式

不管现实多么惨不忍睹,都要持之以恒地相信,这只是黎明前短暂的黑暗而已。不要惶恐眼前的难关迈不过去,不要担心此刻的付出没有回报,别再花时间等待天降好运。真诚做人,努力做事!你想要的,岁月都会给你。设计模式,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

14.设计模式
  • 设计模式是一套被反复使用的、多数人知晓的、经过分类编目的代码设计经验的总结。
14.1.设计模式六大原则
  • 单—职责原则(Single Responsibility Principle):不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。
  • 里氏替换原则(Liskov Substitution Principle):
    • 定义1:如果对每一个类型为 T1的对象 o1,都有类型为 T2 的对象o2,使得以 T1定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。
    • 定义2:所有引用父类的地方必须能透明地使用其子类的对象。即任何父类可以出现的地方,子类一定可以出现
      • 不建议子类重写父类的方法;如果要重写,父类定义为抽象方法
  • 依赖倒置原则(Dependence lnversion Principle):高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。
  • 接口隔离原则(Interface Segregation Principle):客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。
  • 迪米特法则(Law Of Demeter):一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立
  • 开闭原则(Open Closed Principle):在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果
    • 对新增代码开放,对修改代码封闭。
    • 开闭原则无非就是想表达这样一层意思:用抽象构建框架,用实现扩展细节

总结:

  • 单一职责原则告诉我们实现类要职责单一;
  • 里氏替换原则告诉我们不要破坏继承体系;
  • 依赖倒置原则告诉我们要面向接口编程;
  • 接口隔离原则告诉我们在设计接口的时候要精简单一;
  • 迪米特法则告诉我们要降低耦合;
  • 开闭原则是总纲,告诉我们要对扩展开放,对修改关闭。
14.2.单例模式
14.2.1.介绍
  • 单例模式(Singleton Pattern):单例设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
  • 注意:
    1. 单例类只能有一个实例。
    2. 单例类必须自己创建自己的唯一实例。
    3. 单例类必须给所有其他对象提供这一实例
  • 目的:
    • 意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
    • 主要解决:一个全局使用的类频繁地创建与销毁。
    • 何时使用:想控制实例数目,节省系统资源的时候。
    • 如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
    • 关键代码:构造函数是私有的。
  • 实现方式
    1. 构造方法私有
    2. 提供静态方法给外部类来获得实例
    3. 包含本身类型的成员变量
  • 应用实例:
    • 一个班级只有一个班主任。
    • Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。
    • 一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。
  • 优缺点:
    • 优点:
      • 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
      • 避免对资源的多重占用(比如写文件操作)。
    • 缺点:
      • 没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
  • 使用场景:
    • 要求生产唯一序列号。
    • WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
    • 创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。

    注:有时getInstance() 方法中需要使用同步锁 synchronized (Singleton.class) 防止多线程同时进入造成 instance 被多次实例化。

14.2.2.懒汉式线程不安全
  • 对于多个线程调用,无法保证同一个实例,同时,只有真正调用getInstance方法才会完成实例化,否则该对象一直是null
    public class Singleton{
        private static Singleton singleton;
        
        private Singleton(){};
        
        public static Singleton getInstance(){
            if(singleton == null)  singleton = new Singleton();
            return singleton
        }
    }
    
14.2.3.懒汉式线程安全
  • 相比于线程不安全的懒加载,只是在方法中添加了synchronized同步关键字,将其变成同步方法
    public class Singleton{
        private static Singleton singleton;
        
        private Singleton(){};
        
        public synchronized static Singleton getInstance(){
            if(singleton == null)  singleton = new Singleton();
            return singleton
        }
    }
    
  • 静态内部类实现,无需加synchronize关键字:
    public class Singleton{
        
        private Singleton(){};
        
        public synchronized static Singleton getInstance(){
            return singletonInner.getSingleton();
        }
        
        static class SingletonInner{
            private static Singleton singleton;
            static{
                singleton = new Singleton();  //懒汉模式
            }
            public static Singleton getSingleton(){
                return singleton;
            }
        }
    }
    
14.2.4.饿汉式
  • 当该类加载的时候,静态变量就会完成初始化
  • 类加载的方式是按需加载,且只加载一次,因此,在单例类被加载时,就会实例化一个对象并交给自己的引用,供系统使用。即在线程访问单例对象之前就已经创建好了。再加上,由于一个类在整个生命周期中只会被加载一次,因此该单例类只会创建一个实例。线程每次都只能也必定只可以拿到这个唯一的对象。即饿汉式单例天生就是线程安全的。
    public class Singleton{
        private static Singleton singleton = new Singleton();
    
        //或采静态代码块形式,不在声明的时候实例化也可以
        /*
            static{
                singleton = new Singleton();
            }
        */
        
        private Singleton(){};
    
        public static Singleton getInstance(){
            if(singleton == null)  singleton = new Singleton();
            return singleton
        }
    }
    
14.3.工厂模式
14.3.1.介绍
  • 工厂模式(Factory Pattern):工厂设计模式属于创建型模式,它提供了一种创建对象的最佳方式。工厂模式中,在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
  • 意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
  • 主要解决:主要解决接口选择的问题。
  • 何时使用:我们明确地计划不同条件下创建不同实例时。
  • 如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品。
  • 关键代码:创建过程在其子类执行。
  • 应用实例:
    • 需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。
    • Hibernate 换数据库只需换方言和驱动就可以。
  • 优缺点:
    • 优点:
      • 一个调用者想创建一个对象,只要知道其名称就可以了。
      • 扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 3、屏蔽产品的具体实现,调用者只关心产品的接口。
    • 缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
  • 使用场景:
    • 日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。
    • 数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。
    • 设计一个连接服务器的框架,需要三个协议,“POP3”、“IMAP”、“HTTP”,可以把这三个作为产品类,共同实现一个接口。
  • 注意事项:作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。
14.3.2.使用工厂模式
  • 创建一个 Shape 接口和实现 Shape 接口的实体类。定义工厂类 ShapeFactory。
  • FactoryPatternDemo 类使用 ShapeFactory 来获取 Shape 对象。它将向 ShapeFactory 传递信息,以便获取它所需对象的类型。
    //1.创建一个接口
    public interface Shape {
       void draw();
    }
    
    //2.创建接口的实体类
    public class Rectangle implements Shape {
       @Override
       public void draw() {
          System.out.println("Inside Rectangle::draw() method.");
       }
    }
    
    public class Square implements Shape {
       @Override
       public void draw() {
          System.out.println("Inside Square::draw() method.");
       }
    }
    
    public class Circle implements Shape {
       @Override
       public void draw() {
          System.out.println("Inside Circle::draw() method.");
       }
    }
    
    //3.创建一个工厂,用于生成基于给定信息的实体类的对象。
    public class ShapeFactory {
        
       //使用 getShape 方法获取形状类型的对象
       public Shape getShape(String shapeType){
          if(shapeType == null){
             return null;
          }        
          if(shapeType.equalsIgnoreCase("CIRCLE")){
             return new Circle();
          } else if(shapeType.equalsIgnoreCase("RECTANGLE")){
             return new Rectangle();
          } else if(shapeType.equalsIgnoreCase("SQUARE")){
             return new Square();
          }
          return null;
       }
    }
    
    //4.使用该工厂,通过传递类型信息来获取实体类的对象。
    public class FactoryPatternDemo {
     
       public static void main(String[] args) {
          ShapeFactory shapeFactory = new ShapeFactory();
     
          //获取 Circle 的对象,并调用它的 draw 方法
          Shape shape1 = shapeFactory.getShape("CIRCLE");
          //调用 Circle 的 draw 方法
          shape1.draw();
     
          //获取 Rectangle 的对象,并调用它的 draw 方法
          Shape shape2 = shapeFactory.getShape("RECTANGLE");
          //调用 Rectangle 的 draw 方法
          shape2.draw();
     
          //获取 Square 的对象,并调用它的 draw 方法
          Shape shape3 = shapeFactory.getShape("SQUARE");
          //调用 Square 的 draw 方法
          shape3.draw();
       }
    }
    
    /*
    输出结果
        Inside Circle::draw() method.
        Inside Rectangle::draw() method.
        Inside Square::draw() method.
    */
    
14.4.代理模式
14.4.1.介绍
  • 代理模式(Proxy Pattern):一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。在代理模式中,创建具有现有对象的对象以便向外界提供功能接口。
  • 意图:为其他对象提供一种代理以控制对这个对象的访问。
  • 主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
  • 何时使用:想在访问一个类时做一些控制。
  • 如何解决:增加中间层。
  • 关键代码:实现与被代理类组合。
  • 应用实例:
    • Windows 的快捷方式。
    • 买火车票不一定在火车站买,也可以去代售点。
    • 一张支票或银行存单是账户中资金的代理。支票在市场交易中用来代替现金,并提供对签发人账号上资金的控制。
    • spring aop。
  • 优缺点:
    • 优点:
      • 职责清晰。
      • 高扩展性。
      • 智能化。
    • 缺点:
      • 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
      • 实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
  • 使用场景:按职责来划分,通常有以下使用场景:
    • 远程代理。
    • 虚拟代理。
    • Copy-on-Write 代理。
    • 保护(Protect or Access)代理。
    • Cache代理。
    • 防火墙(Firewall)代理。
    • 同步化(Synchronization)代理。
    • 智能引用(Smart Reference)代理。
  • 注意事项:
    • 和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
    • 和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。
14.4.2.静态代理模式
  • 创建一个 Image 接口和实现了 Image 接口的实体类。ProxyImage 是一个代理类,减少 RealImage 对象加载的内存占用。ProxyPatternDemo 类使用 ProxyImage 来获取要加载的 Image 对象,并按照需求进行显示。
    //1.创建一个接口
    public interface Image {
       void display();
    }
    
    
    //2.创建实现接口的实体类,委托类
    public class RealImage implements Image {
     
       private String fileName;
     
       public RealImage(String fileName){
          this.fileName = fileName;
          loadFromDisk(fileName);
       }
     
       @Override
       public void display() {
          System.out.println("Displaying " + fileName);
       }
     
       private void loadFromDisk(String fileName){
          System.out.println("Loading " + fileName);
       }
    }
    
    
    
    public class ProxyImage implements Image{
     
       private RealImage realImage;
       private String fileName;
     
       public ProxyImage(String fileName){
          this.fileName = fileName;
       }
     
       @Override
       public void display() {
          if(realImage == null){
             realImage = new RealImage(fileName);
          }
          realImage.display();
       }
    }
    
    
    //3.当被请求时,使用 ProxyImage 来获取 RealImage 类的对象
    public class ProxyPatternDemo {
       
       public static void main(String[] args) {
          Image image = new ProxyImage("test_10mb.jpg");
     
          // 图像将从磁盘加载
          image.display(); 
          System.out.println();
          // 图像不需要从磁盘加载
          image.display();  
       }
    }
    
    
    /*
    输出结果
        Loading test_10mb.jpg
        Displaying test_10mb.jpg
    
        Displaying test_10mb.jpg
    */
    
14.4.3.JDK动态代理
  • Image接口和RealImage实现类不变
    //1.创建一个接口
    public interface Image {
       void display();
    }
    
    
    //2.创建实现接口的实体类,委托类
    public class RealImage implements Image {
     
       private String fileName;
     
       public RealImage(String fileName){
          this.fileName = fileName;
          loadFromDisk(fileName);
       }
     
       @Override
       public void display() {
          System.out.println("Displaying " + fileName);
       }
     
       private void loadFromDisk(String fileName){
          System.out.println("Loading " + fileName);
       }
    }
    
    
    //3.使用JDK动态代理
    public class JDKDynamicProxy {
        public static void main(String[] args) {
            
            //在main方法中保存生成的字节码文件,保存到working directory
            //System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
            
            Image image = new RealImage("test_10mb.jpg");
            
            //生成一个JDK动态代理类的代理对象,和RealImage类同样都实现了Image接口
            //注意,这里仅仅是生成一个代理对象,仅仅是一个声明,所以应该是先执行display方法,然后才会回调invoke方法
            Image jdkProxy = (Image) Proxy.newProxyInstance(
                //getInterfaces可以获得这个类所实现的所有接口
                RealImage.class.getClassLoader(),RealImage.class.getInterfaces(),
                    new InvocationHandler(){
                        /**
                        * @param proxy 代理对象
                        * @param method 正在执行的方法
                        * @param args 正在执行的方法所携带的参数
                        * @return Object 委托类方法执行的结果
                        */
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            //通过调试查看方法调用
                            //System.out.printlm(method.getName());
                            System.out.println("请稍等");
                            Object invoke = method.invoke(image, args);
                            System.out.println("展示完毕");
                            return invoke;
                        }
                    });
            
    //          //lambda表达式写法,更为简洁
    //        Image imageProxy = (Image) Proxy.newProxyInstance(RealImage.class.getClassLoader(),RealImage.class.getInterfaces(),
    //                  //arg名称为了防止和main方法中的参数args重名,取任意均可
    //                (proxy,method,arg)-> {
    //                    System.out.println("请稍等");
    //                    Object invoke = method.invoke(image, arg);
    //                    System.out.println("展示完毕");
    //                    return invoke;
    //                });
            //在这里,代理对象是imageProxy,方法名是display,参数是null,其实是提供给invoke方法的
            jdkProxy.display();
        }
    }
    
14.4.4.Cglib动态代理
  • Image接口和RealImage实现类不变
    //1.创建一个接口
    public interface Image {
       void display();
    }
    
    
    //2.创建实现接口的实体类,委托类
    public class RealImage implements Image {
     
       private String fileName;
     
       public RealImage(String fileName){
          this.fileName = fileName;
          loadFromDisk(fileName);
       }
     
       @Override
       public void display() {
          System.out.println("Displaying " + fileName);
       }
     
       private void loadFromDisk(String fileName){
          System.out.println("Loading " + fileName);
       }
    }
    
    
    //3.maven导入依赖
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.2.12</version>
    </dependency>
        
        
    //4.使用Cglib动态代理
    public class CglibDynamicProxy {
        public static void main(String[] args) {
            
            //设置编译后的代理对象的class文件保存路径
            //System.setProperty(DebuggingClassWriter,DEBUG_LOCATION_PROPERTY,"D:\\temp");
            
            final Image image = new RealImage("test_10mb.jpg");
            
            //cglib动态代理方式继承了Image的实现类,即继承了RealImage类,与JDK动态代理方式不同
            //因此在这里,通过RealImage或Image来接收均可
            //class参数使用接口和实现类均可,InvocationHandler接口和JDK的不是同一个
            Image cglibProxy = (Image) Enhancer.create(Image.class, new InvocationHandler() {
                public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                    //通过调试查看方法调用
                    //System.out.printlm(method.getName());
                    System.out.println("请稍等");
                    Object invoke = method.invoke(image, objects);
                    System.out.println("展示完毕");
                    return invoke;
                }
            });
            
    //        Image cglibProxy = (Image) Enhancer.create(Image.class,(InvocationHandler)(o,method,objects)->{
    //            System.out.println("请稍等");
    //            Object invoke = method.invoke(image, objects);
    //            System.out.println("展示完毕");
    //            return invoke;
    //        });
    
            cglibProxy.display();
        }
    }
    

    目标对象实现了某个接口,可以使用JDK动态代理需要或Cglib动态代理方式均可,否则只能使用cglib方式。

14.5.建造者模式
14.5.1.介绍
  • 建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。
  • 意图:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
  • 主要解决:主要解决在软件系统中,有时候面临着”一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
  • 何时使用:一些基本部件不会变,而其组合经常变化的时候。
  • 如何解决:将变与不变分离开。
  • 关键代码:建造者:创建和提供实例,导演:管理建造出来的实例的依赖关系。
  • 应用实例:
    • 去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的”套餐”。
    • JAVA 中的 StringBuilder的append方法
  • 优缺点:
    • 优点:
      • 建造者独立,易扩展。
      • 便于控制细节风险。
    • 缺点:
      • 产品必须有共同点,范围有限制。
      • 如内部变化复杂,会有很多的建造类。
  • 使用场景:
    • 需要生成的对象具有复杂的内部结构。
    • 需要生成的对象内部属性本身相互依赖。
  • 注意事项:与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。
14.5.2.设计
  • 假设一个快餐店的商业案例,其中,一个典型的套餐可以是一个汉堡(Burger)和一杯冷饮(Cold drink)。汉堡(Burger)可以是素食汉堡(Veg Burger)或鸡肉汉堡(Chicken Burger),它们是包在纸盒中。冷饮(Cold drink)可以是可口可乐(coke)或百事可乐(pepsi),它们是装在瓶子中。
  • 我们将创建一个表示食物条目(比如汉堡和冷饮)的 Item 接口和实现 Item 接口的实体类,以及一个表示食物包装的 Packing 接口和实现 Packing 接口的实体类,汉堡是包在纸盒中,冷饮是装在瓶子中。
  • 我们创建一个 Meal 类,带有 ItemArrayList 和一个通过结合 Item 来创建不同类型的 Meal 对象的 MealBuilderBuilderPatternDemo 类使用 MealBuilder 来创建一个 Meal
//1.创建一个表示食物条目和食物包装的接口
public interface Item {
   public String name();
   public Packing packing();
   public float price();    
}

public interface Packing {
   public String pack();
}


//2.创建实现 Packing 接口的实体类
public class Wrapper implements Packing {
 
   @Override
   public String pack() {
      return "Wrapper";
   }
}

public class Bottle implements Packing {
 
   @Override
   public String pack() {
      return "Bottle";
   }
}


//3.创建实现 Item 接口的抽象类,该类提供了默认的功能
public abstract class Burger implements Item {
 
   @Override
   public Packing packing() {
      return new Wrapper();
   }
 
   @Override
   public abstract float price();
}

public abstract class ColdDrink implements Item {
 
    @Override
    public Packing packing() {
       return new Bottle();
    }
 
    @Override
    public abstract float price();
}


//4.创建扩展了 Burger 和 ColdDrink 的实体类。
public class VegBurger extends Burger {
 
   @Override
   public float price() {
      return 25.0f;
   }
 
   @Override
   public String name() {
      return "Veg Burger";
   }
}

public class ChickenBurger extends Burger {
 
   @Override
   public float price() {
      return 50.5f;
   }
 
   @Override
   public String name() {
      return "Chicken Burger";
   }
}

public class Coke extends ColdDrink {
 
   @Override
   public float price() {
      return 30.0f;
   }
 
   @Override
   public String name() {
      return "Coke";
   }
}

public class Pepsi extends ColdDrink {
 
   @Override
   public float price() {
      return 35.0f;
   }
 
   @Override
   public String name() {
      return "Pepsi";
   }
}


//5.创建一个 Meal 类,带有上面定义的 Item 对象。
public class Meal {
   private List<Item> items = new ArrayList<Item>();    
 
   public void addItem(Item item){
      items.add(item);
   }
 
   public float getCost(){
      float cost = 0.0f;
      for (Item item : items) {
         cost += item.price();
      }        
      return cost;
   }
 
   public void showItems(){
      for (Item item : items) {
         System.out.print("Item : "+item.name());
         System.out.print(", Packing : "+item.packing().pack());
         System.out.println(", Price : "+item.price());
      }        
   }    
}


//6.创建一个 MealBuilder 类,实际的 builder 类负责创建 Meal 对象
public class MealBuilder {
 
   public Meal prepareVegMeal (){
      Meal meal = new Meal();
      meal.addItem(new VegBurger());
      meal.addItem(new Coke());
      return meal;
   }   
 
   public Meal prepareNonVegMeal (){
      Meal meal = new Meal();
      meal.addItem(new ChickenBurger());
      meal.addItem(new Pepsi());
      return meal;
   }
}


//7.BuiderPatternDemo 使用 MealBuilder 来演示建造者模式(Builder Pattern)
public class BuilderPatternDemo {
   public static void main(String[] args) {
      MealBuilder mealBuilder = new MealBuilder();
 
      Meal vegMeal = mealBuilder.prepareVegMeal();
      System.out.println("Veg Meal");
      vegMeal.showItems();
      System.out.println("Total Cost: " +vegMeal.getCost());
 
      Meal nonVegMeal = mealBuilder.prepareNonVegMeal();
      System.out.println("\n\nNon-Veg Meal");
      nonVegMeal.showItems();
      System.out.println("Total Cost: " +nonVegMeal.getCost());
   }
}

/*
输出结果:
    Veg Meal
    Item : Veg Burger, Packing : Wrapper, Price : 25.0
    Item : Coke, Packing : Bottle, Price : 30.0
    Total Cost: 55.0


    Non-Veg Meal
    Item : Chicken Burger, Packing : Wrapper, Price : 50.5
    Item : Pepsi, Packing : Bottle, Price : 35.0
    Total Cost: 85.5
*/

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/181067.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!