专业课程-软件系统设计与体系结构

内容纲要

软件系统设计及体系结构

第一章-软件设计模式基础


第二章-创建型设计模式

单例模式

介绍:为其它类提供自己创建唯一实例

  1. 实例

    • 多线程共享打印机
    • 多终端共享打印机
  2. 优点

    • 减少内存开销
    • 避免对资源的多重占用
  3. 缺点

    • 没有接口,无法继承,与单一职责原则冲突
  4. 实例代码

    //SingleObject.java
    package 专业课程.软件系统设计及体系结构.设计模式实例.单例模式;
    
    public class SingleObject{
        //创建SingleObject类的一个对象
        private static SingleObject instance = new SingleObject();
        //构造方法为private,这样该类就不会实例化
        private SingleObject (){};
        //获取外部唯一可用的对象实例
        public static SingleObject getInstance(){
            return instance;
        }
        public void showMessage(){
            System.out.println("hello 单例模式");
        }
    }
    //SingletonPatternDemo.java
    package 专业课程.软件系统设计及体系结构.设计模式实例.单例模式;
    
    public class SingletonPatternDemo{
        public static void main(String[] args) {
            //不合法的构造函数
            //SingleObject() 是不可见的
            //SingleObject object = new SingleObject();
            //正确的构造方法,获取唯一可用的对象
            SingleObject object = SingleObject.getInstance();
            //显示消息
            object.showMessage();
        }
    }
  5. 多线程下的实现代码

    • 懒汉式,线程不安全
      延迟加载,在需要的时候才创建对象,在多线程下无法正常工作

      //饱汉式线程不安全
      public class Singleton{
          private static Singleton instance;  //声明对象
          private Singleton(){};  //构造方法
          public static Singleton getInstance(){
              if(instance == null){
                  instance = new Singleton(); //创建对象
              }
              return instance;
          }
      }
    • 懒汉式,线程安全
      能够在多线程下正常工作,但是效率很低,现在的软件运行速度很快,一般不产生线程冲突,可判断程序是否会产生线程冲突而使用synchronized关键字

      //饱汉式线程不安全
      public class Singleton{
          private static Singleton instance;  //声明对象
          private Singleton(){};  //构造方法
          public static synchronized Singleton getInstance(){
              if(instance == null){
                  instance = new Singleton(); //创建对象
              }
              return instance;
          }
      }
    • 饿汉式
      类加载时就初始化类对象,浪费内存

      public class SingleObject{
          //创建SingleObject类的一个对象
          private static SingleObject instance = new SingleObject();
          //构造方法为private,这样该类就不会实例化
          private SingleObject (){};
          //获取外部唯一可用的对象实例
          public static SingleObject getInstance(){
              return instance;
          }
          public void showMessage(){
              System.out.println("hello 单例模式");
          }
      }

工厂模式

在创建对象时不会对客户端暴露创建逻辑,通过使用一个共同的接口来指向新创建的对象。

  1. 介绍

    • 意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行
    • 主要作用:解决接口选择的问题
    • 使用场景
      • 日志记录器:本地硬盘、系统事件、远程服务器等
      • 数据库访问
      • 连接服务器的框架,三个协议:POP3、IMAP、HTTP
  2. 优点

    • 调用者只需要知道名称
    • 可拓展性高,增加产品只需拓展一个工厂类
    • 屏蔽产品的具体实现,调用者只需关心产品的接口
  3. 缺点

    • 每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖
  4. 实现代码

    //客户端类,用户只需要知道形状的标识符而不需要知道逻辑
    public class FactoryPatternDemo{
        public static void main(String[] args) {
            ShapeFactory shapeFactory = new ShapeFactory();
            Shape shape1 = shapeFactory.getShape("CIRCLE");
            shape1.draw();
            Shape shape2 = shapeFactory.getShape("RECTANGLE");
            shape2.draw();
            Shape shape3 = shapeFactory.getShape("SQUARE");
            shape3.draw();
        }
    }
    //工厂类,通过使用一个共同的接口来创建新的对象
    public class ShapeFactory{
        //使用getShape方法获取形状类型的对象
        public Shape getShape(String shapeType){
            if(shapeType == null) return null;
            if(shapeType.equalsIgnoreCase("CIRCLE")){
                return new Circle();
            }
            if(shapeType.equalsIgnoreCase("RECTANGLE")){
                return new Rectangle();
            }
            if(shapeType.equalsIgnoreCase("SQUARE")){
                return new Square();
            }
            return null;
        }
    }
    //共用的接口类
    public interface Shape{
        void draw();
    }
    //圆形类
    public class Circle implements Shape{
        public Circle(){};
        public void draw(){
            System.out.println("Inside Circle::draw() method");
        }
    }
    //矩形类
    public class Rectangle implements Shape{
        public Rectangle(){};
        public void draw(){
            System.out.println("Inside Rectangle::draw() method");
        }
    }
    //正方形类
    public class Square implements Shape{
        public void draw(){
            System.out.println("Inside Square::draw() method");
        }
    }
  5. 抽象工厂

建造者模式(Builder Pattern)

使用多个简单的对象一步步构建成一个复杂的对象,提供了一种创建对象的最佳方式

一个Builder类会一步一步构造最终的对象,该Builder类是独立于其它对象的

  1. 介绍
    • 意图:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示
    • 主要解决:一个复杂对象的创建工作,其通常由各个部分的子对象用一定的算法构成;(由于需求变化,各个部位的对象经常面临剧烈变化,而将它们组合在一起的算法却相对稳定)
    • 何时使用:一些基本部件不会变,而其组合经常变化的时候
    • 应用实例:
      • 肯德基:汉堡、可乐、薯条等对象是不变的,而其套餐组合是经常变化的
      • Java中的StringBuilder

dad


第三章-结构型模式

适配器模式

  1. 概念

    • 作为两个不兼容接口之间的桥梁,结合了两个独立接口的功能
    • 也称为包装样式或者包装,将一个类的接口转换成用户所期待的
    • 一个适配使得因接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存在的类中
  2. 介绍

    • 主要解决:
      • 在软件系统中,常常要将一些现存的对象放到新的环境中,而新环境要求的接口是现对象不能满足的
    • 何时使用:
      • 系统需要使用现有类,而此类接口不符合系统需要
      • 想要建立一个可重复类,用于一些彼此之间无太大关系的类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口
      • 通过接口转换,将一个类插入另一个类系中
    • 应用实例:
      • 电饭煲:日本电饭煲的电源接口标准是110V电压,而国内是220V,为使用日本电饭煲,需要一个电源适配器
  3. 实现代码

    //测试程序
    public class PlayerDemo {
        public static void main(String[] args) {
            AudioPlayer player = new AudioPlayer();
            player.play("mp3","Only you.mp3");
            player.play("vlc","Oh no.vlc");
            player.play("mp4","Yes sir.mp4");
            player.play("avi","Who am I.avi");
        }
    }
    //可播放mp3的接口
    public interface MediaPlayer {
        void play(String audioType,String filename);
    }
    //可播放mp4和vlc格式的接口
    public interface AdvancedPlayer {
        void play_mp4(String filename);
        void play_vlc(String filename);
    }
    //AudioPlayer内置支持播放Mp3,适配后支持播放mp3和vlc
    public class AudioPlayer implements MediaPlayer {
        MediaAdapter mediaAdapter;
    
        @Override
        public void play(String audioType, String filename) {
            if (audioType.equalsIgnoreCase("mp3")) {
                System.out.println("Playing Mp3 file. Name:" + filename);
            } 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");
            }
        }
    }
    //实现Mp4和Vlc播放接口的类
    public class Mp4Player implements AdvancedPlayer{
        @Override
        public void play_mp4(String filename) {
            System.out.println("Playing Mp4 file. Name:"+filename);
        }
        @Override
        public void play_vlc(String filename) {
            //nothing
        }
    }
    
    public class VlcPlayer implements AdvancedPlayer{
        @Override
        public void play_mp4(String filename) {
            //nothing to do
        }
        @Override
        public void play_vlc(String filename) {
            System.out.println("Playing Vlc file. Name: " + filename);
        }
    }
    //适配  
    public class MediaAdapter implements MediaPlayer{
    
        AdvancedPlayer advancedPlayer;
    
        public MediaAdapter(String audiotype){
            if(audiotype.equalsIgnoreCase("vlc")){
                advancedPlayer = new VlcPlayer();
            }else if(audiotype.equalsIgnoreCase("mp4")){
                advancedPlayer = new Mp4Player();
            }
        }
        @Override
        public void play(String audioType, String filename) {
            if(audioType.equalsIgnoreCase("vlc")){
                advancedPlayer.play_vlc(filename);
            }else if(audioType.equalsIgnoreCase("mp4")){
                advancedPlayer.play_mp4(filename);
            }
        }
    }

组合模式

  1. 概念
    • 为了一致对待容器节点和叶子节点,组合多个对象形成树形结构以表示具有整体-部分关系的层次结构,故称为组合模式
  2. 模式结构
    • Component抽象构件,叶子构件和容器构件的接口或抽象类
    • Leaf叶子构件,叶子结点没有子结点
    • Composite容器构件,容器结点可以有子结点,子结点也可以为叶子构件
  3. 适用场景
    • 在具有整体和部分的层级结构中,希望通过一种方式忽略整体和部分的差异,客户端可以一致地对待它们
    • 在一个使用面向对象语言开发的系统中需要处理一个树形结构
    • 在一个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,需要增加一些新的类型
  4. 应用实例
    • 公司职员的组织结构
  5. 实现代码

    public class Employee {
        private String name;
        private String dept;
        private int salary;
        //叶子结点subordinate为空
        //容器结点subordinate不为空
        private List subordinates;
    
        //构造函数
        public Employee(String name,String dept,int salary){
            this.name = name;
            this.dept = dept;
            this.salary = salary;
            subordinates = new ArrayList();
        }
        public void add(Employee e){
            subordinates.add(e);
        }
        public void remove(Employee e){
            subordinates.remove(e);
        }
        public List getSubordinates(){
            return subordinates;
        }
        public String toString(){
            return ("Employee:[ Name: " +
                    name + ",dept:"+dept+",salart:"+
                    salary+"]");
        }
    }
    public class CompositionPatternDemo {
        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 clerk1 = new Employee("Laura","Marketing",10000);
            Employee clerk2 = new Employee("Bob","Marketing",10000);
    
            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);
    
            headMarketing.add(clerk1);
            headMarketing.add(clerk2);
    
            printEmployee(CEO); //递归输出层级结构
        }
        //递归输出所有层级
        static void printEmployee(Employee employee){
            System.out.println(employee);
            for(Employee e:employee.getSubordinates()){
                printEmployee(e);
            }
        }
    }

享元模式(Flyweight Pattern)

  1. 概念
    • 主要用于减少创建对象的数量,以减少内存占用和提高性能
    • 尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象
  2. 介绍
    • 意图
      • 运用共享技术有效地支持大量细粒度的对象
    • 主要解决
      • 在有大量对象时,可能会造成内存溢出,我们把其中共同的部分抽象出来
      • 如果有相同的业务请求,直接返回早内存中已有的对象,避免重新创建
    • 如何解决
      • 用唯一标志码判断,如果在内存中有,则返回这个唯一标志码所标识的对象
    • 使用场景
      • 系统有大量相似对象
      • 需要缓冲池的场景
  3. 实例
    通过享元模式减少创建的课本对象,以提供给指定数量的学生借阅

    • Book接口类和Textbook类

      public interface Book {
          //输出书本的状态信息
          void showStatus();
      }
      
      public class Textbook implements Book{
          //书本名称
          private String name;
          //该书的借阅次数
          private int barrowNum;
      
          Textbook(String name){
              this.name = name;
              barrowNum = 0;
          }
          //计书本的借阅次数
          public void addBarrowNum(){
              barrowNum++;
          }
      
          public void showStatus(){
              System.out.println("书本的名称:《" + name + "》"
                      +",被借阅的次数:" + barrowNum);
          }
      
          public void setName(String name) {
              this.name = name;
          }
      }
    • BookFactory工厂类。

      提供所需的书本,区别于工厂模式,采用了HashMap的数据结构
      如果哈希表中已经存在所需书本对象,则直接返回该对象;
      若哈希表中不存在所需书本对象,则创建书本对象,以此来达到减少创建对象的目的。

      public class BookFactory {
          //哈希表中存储所有的对象
          //区别于工厂模式,用哈希表减少创建对象的数量
          private static final HashMap bookMap
                  = new HashMap();
          public static Book getTextBook(String name){
              //先在哈希表中查找有没有已存在的课本对象
              Textbook textbook = (Textbook)bookMap.get(name);
              if(textbook == null){ //没有则新建
                  textbook = new Textbook(name);
                  bookMap.put(name,textbook);
                  System.out.println("书库中不存在,为您订购了一本书:<<" + name + ">>");
              }
              //计该书的借阅次数
              textbook.addBarrowNum();
              return textbook;
          }
      }
    • LibraryDemo 测试主类

      public class ShapeFlyweightDemo {
          private static final String colors[] =
                  {"Red","Green","Blue","White","Black"};
          public static void main(String[] args) {
              for (int i = 0; i < 20; ++i){
                  Circle circle = (Circle)ShapeFactory.getCircle(getRandomColor());
                  circle.setX(getRandomX());
                  circle.setY(getRandomY());
                  circle.setRadius(100);
                  circle.draw();
              }
          }
          //返回随机的颜色字符串
          private static String getRandomColor(){
              return colors[(int)(Math.random()*colors.length)];
          }
          private static int getRandomX(){
              return (int)(Math.random()*100);
          }
          private static int getRandomY(){
              return (int)(Math.random()*100);
          }
      }

发表评论