外观 & 桥接模式实验1

外观模式设计实验

某软件公司欲开发一个可应用于多个软件的文件加密模块。该模块可以对文件中的数据进行加密并将加密之后的数据存储在一个新文件中。具体的流程包括 3 个部分,分别是读取源文件、加密、保存加密之后的文件。其中读取文件和保存文件使用流来实现;加密操作通过求模运算实现。这 3 个操作相对独立,为了实现代码的独立重用,让设计更符合单一职责原则,这 3 个操作的业务代码封装在 3 个不同的类中。

某软件公司开发人员独立实现了这 3 个具体业务类:FileReader 类用于读取文件 CipherMachine 类用于对数据进行加密;FileWriter 用于保存文件。由于该文件加密模块的通用性,它在某公司开发的多款软件中都得以使用,包括财务管理软件、公文审批系统、邮件管理系统等,如下图所示。

在上图中,不难发现,在每一次使用这 3 个类时都需要编写代码与它们逐个进行交互,3 个类和客户端代码如下:

FileReader.java

CipherMachine.java

FileWriter.java

Client.java

测试源文件src.txt内容如下:

"At a time when the world is entering a period of uncertainty and transformation, the significance and influence of the China-Russia relationship goes far beyond the bilateral scope," Foreign Ministry spokesman Wang Wenbin said on Friday.

It will be Xi's first overseas trip since he was elected Chinese president last week and also his first visit to Russia since the Ukraine crisis broke out in February last year.

According to the spokesman, during the visit, Xi and Russian President Vladimir Putin will have an in-depth exchange of views on bilateral relations and major international and regional affairs.

某公司开发人员通过分析发现,该方案虽然能够实现预期功能,但存在以下两个问题:

(1)FileReader 类、CipherMachine 类和 FileWriter 类经常会作为一个整体同时出现。但是如果按照上述方案进行设计和实现,在每一次使用这 3 个类时,客户端代码需要与它们逐个进行交互,导致客户端代码较为复杂,且在每次使用它们时很多代码都将重复出现。

(2)如果需要更换一个加密类,例如将 CipherMachine 类改为NewCipherMachine 类,则所有使用该文件加密模块的代码都需要进行修改,系统维护难度增大,灵活性和可扩展性较差。

为了解决这两个问题,可以使用外观模式对文件加密模块进行重构,在客户端代码和业务类之间增加一个外观类,由外观类来封装与业务类之间的交互,而客户端只需与外观类交互即可。

为了降低系统的合度,封装与多个子系统进行交互的代码,请使用外观模式来重构文件加密模块的设计。

答案举例:

class EncryptFacade

    {

         private FileReader reader;

        private CipherMachine cipher;

        private FileWriter writer;

        public EncryptFacade()

        {

            reader = new FileReader();

            cipher = new CipherMachine();

            writer = new FileWriter();

        }

         public void FileEncrypt(string fileNameSrc, string fileNameDes)

        {

            string plainStr = reader.Read(fileNameSrc);

            string encryptStr = cipher.Encrypt(plainStr);

            writer.Write(encryptStr, fileNameDes);

        }

    }

class CipherMachine

    {

       public string Encrypt(string plainText)

       {

       Console.Write("数据加密,将明文转换为密文:");

       string es = "";

            char[] chars = plainText.ToCharArray();

       foreach(char ch in chars)

            {

                string c = (ch % 7).ToString();

           es += c;

       }

            Console.WriteLine(es);

       return es;

    }

    }

class FileWriter

    {

        public void Write(string encryptStr,string fileNameDes)

        {

       Console.WriteLine("保存密文,写入文件。");

            FileStream fs = null;

       try

            {

               fs = new FileStream(fileNameDes, FileMode.Create);

                byte[] str = Encoding.Default.GetBytes(encryptStr);

                fs.Write(str,0,str.Length);

                fs.Flush();

               fs.Close();

       }    

       catch(FileNotFoundException e)

            {

        Console.WriteLine("文件不存在!");

       }

       catch(IOException e)

            {

                Console.WriteLine(e.Message);

           Console.WriteLine("文件操作错误!");

       }        

        }

    }

class FileReader

    {

        public string Read(string fileNameSrc)

        {

       Console.Write("读取文件,获取明文:");

            FileStream fs = null;

            StringBuilder sb = new StringBuilder();

       try

            {

                fs = new FileStream(fileNameSrc, FileMode.Open);

                int data;

               while((data = fs.ReadByte())!= -1)

                {

            sb = sb.Append((char)data);

               }

               fs.Close();

               Console.WriteLine(sb.ToString());

       }

       catch(FileNotFoundException e)

            {

           Console.WriteLine("文件不存在!");

       }

       catch(IOException e)

            {

           Console.WriteLine("文件操作错误!");

       }

       return sb.ToString();

        }

    }

public class Client

    {

        public static void Main(string[] args) throws Exception

        {

            EncryptFacade ef = new EncryptFacade();

            ef.FileEncrypt("src.txt", "des.txt");

        }

    }

桥接模式设计实验

某软件公司欲开发一个跨平台图像浏览系统,要求该系统能够显示 BMP、JPEG、GIF、PNG等多种格式的文件,并且能够在 Windows、Linux,Mac OS等多个操作系统上运行。该系统首先将各种格式的文件解析为像素矩阵(Matrix),然后将像素矩阵显示在屏幕上,在不同的操作系统中可以调用不同的绘制函数来绘制像素矩阵。该系统需具有较好的扩展性以支持新的文件格式和操作系统。

某软件公司的开发人员针对上述要求,提出了一个初始设计方案,其基本结构如下图所示。

在上图的初始设计方案中,使用了一种多层继承结构。Image 是抽象父类,而每一种类型的图像类,例如 BMPImage、JPEGImage 等作为其直接子类,不同的图像文件格式具有不同的解析方法,可以得到不同的像素矩阵。由于每一种图像需要在不同的操作系统中显示,而不同的操作系统在屏幕上显示像素矩阵又有所差异,因此需要为不同的图像类再提供一组在不同操作系统中显示的子类,例如为 BMPImage 提供 3 个子类 BMPWindowsImp、BMPLinuxImp、BMPMacOSImp,分别用于Windows、Linux 和 Mac OS等操作系统下图像显示。

现在对该设计方案进行分析,发现存在以下两个主要问题:

(1)由于采用了多层继承结构,导致系统中类的个数急剧增加。上图中,在各种图像的操作系统实现层提供了 12 个具体类,加上各级抽象层的类,系统中类的总个数达到了17个。在该设计方案中,具体层的类的个数 = 所支持的图像文件格式数×所支持的操作系统数。

(2)系统扩展麻烦。由于每一个具体类既包含图像文件格式信息,又包含操作系统信息,因此无论是增加新的图像文件格式还是增加新的操作系统,都需要增加大量的具体类。例如,要在上图所示的设计中增加一种新的图像文件格式 TIF ,则需要增加 3 个具体类来实现该格式图像在 3 种不同操作系统的显示。如果增加一个新的操作系统Android ,为了在该操作系统下能够显示各种类型的图像,需要增加 4 个具体类。这将导致系统变得非常庞大,增加运行和维护开销。

通过分析可以得知,该系统存在两个独立变化的维度:图像文件格式和操作系统。如何将各种不同类型的图像文件解析为像素矩阵与图像文件格式本身相关,而如何在屏幕上显示像素矩阵则仅与操作系统相关。正因为上图所示结构将这两种职责集中在一个类中导致系统扩展麻烦。从类的设计角度分析,具体类 BMPWindowsImp、BMPLinuxImp 和 BMPMacOSImp 等违反了单一职责原则。因为有不止一个引起它们变化的原因。它们将图像文件解析和像素矩阵显示这两种完全不同的职责耦合在一起,任意一个职责发生改变都需要修改它们,因此系统扩展困难。

要解决上面的问题,需要将图像文件格式(对应图像格式的解析)与操作系统(对应像素矩阵的显示)两个维度分离,使得它们可以独立变化,增加新的图像文件格式或者操作系统时都对另一个维度不造成任何影响。

请使用桥接模式来重构跨平台图像浏览系统的设计。

答案举例:

abstract class Image {

    protected ImageImp imp;

    public void setImageImp(ImageImp imp) {

        this.imp = imp;

    }

    public abstract void parseFile(String fileName);

}

interface ImageImp {

    public void doPaint(Matrix m);  

}

class WindowsImp implements ImageImp {

    public void doPaint(Matrix m) {

        System.out.print("在Windows操作系统中显示图像:");

    }

}

class LinuxImp implements ImageImp {

    public void doPaint(Matrix m) {

        System.out.print("在Linux操作系统中显示图像:");

    }

}

class UnixImp implements ImageImp {

    public void doPaint(Matrix m) {

        System.out.print("在Unix操作系统中显示图像:");

    }

}

class JPGImage extends Image {

    public void parseFile(String fileName) {

        Matrix m = new Matrix();

        imp.doPaint(m);

        System.out.println(fileName + ",格式为JPG。");

    }

}

class PNGImage extends Image {

    public void parseFile(String fileName) {

        Matrix m = new Matrix();

        imp.doPaint(m);

        System.out.println(fileName + ",格式为PNG。");

    }

}

class BMPImage extends Image {

    public void parseFile(String fileName) {

        Matrix m = new Matrix();

        imp.doPaint(m);

        System.out.println(fileName + ",格式为BMP。");

    }

}

class GIFImage extends Image {

    public void parseFile(String fileName) {

        Matrix m = new Matrix();

        imp.doPaint(m);

        System.out.println(fileName + ",格式为GIF。");

    }

  • 48
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值