Skip to content

Yangxc0414/Design-Patterns-Exercises

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 

Repository files navigation

设计模式作业题和真题详解

目录

  1. 设计模式学习指南
  2. 作业一
  3. 作业二
  4. 作业三
  5. 作业四
  6. 历年真题

设计模式学习指南

这是一份设计模式学习指南,包含多个作业和真题练习,帮助你掌握各种设计模式的应用。


作业一

题目描述

小王为某管理信息系统的用户管理模块设计了如图所示接口。

  1. 请你从面向对象设计原则的角度详细分析小王的设计存在什么问题?
  2. 请你采用面向对象方法帮小王重新设计,使得新设计能够符合面向对象设计原则。简要说明你的设计思想,画出UML类图。

作业一接口设计

分析结果

该设计违背了接口隔离原则和单一职责原则。

重构方案

该接口进行重构设计,将其中的一些方法封装在两个不同的接口中,确保每一个接口使用起来都较为方便。如下面两种重构方案皆可。

重构方案


第二题

客户请小王编写一个从键盘读入字符并输出到打印机的程序。小王使用结构化的设计方法,编写了如下代码。该程序包含3个子程序,Copy子程序从ReadKeyboard子程序中获取字符,并把字符传递给WritePrinter子程序。几个月后,客户说有时希望Copy程序能从纸带读入机中读入字符。并且,在未来可能还会从其它种类的输入设备中读入字符,也可能输出到其它种类的输出设备。小王希望他写出的代码既能满足上述要求,又不用每次都改写Copy的实现。

请采用面向对象方法通过恰当的设计模式对小王的代码进行重构。请写出你所选择的设计模式,画出类图,并给出核心代码。

设计方案

class Reader {
public:
    virtual char Read() = 0;
};

class Writer {
public:
    virtual void Write(char c) = 0;
};

class KeyboardReader : public Reader {
public:
    char Read() override {
        char c;
        cin >> c;
        return c;
    }
};

class PaperReader : public Reader {
public:
    char Read() override {
        // 从纸带读入机读取字符
        return ' ';
    }
};

class PrintWriter : public Writer {
public:
    void Write(char c) override {
        cout << c;
    }
};

KeyboardReader DefaultReader;
PrintWriter DefaultWriter;

void Copy(Reader &reader = DefaultReader, Writer &writer = DefaultWriter) {
    char c;
    while ((c = reader.Read()) != EOF) {
        writer.Write(c);
    }
}

第三题

一个开宝箱游戏的基本描述为:游戏中有多种类型的人物(Role),如战士(Solider)、魔法师(Mage)等,主角的类型只能选择其中一种,且游戏中不再更改。游戏中还有各种宝箱(Box),如装有不同数目金钱的宝箱、装有毒物的宝箱等。当任一种类型的主角打开装有金钱的宝箱时,宝箱中的金钱会增加给主角,同时宝箱的金钱数目变成0;当战士打开装有毒物的宝箱时,战士的生命值(HP)会减少10%,但金钱(Money)会增加40%;当魔法师打开装有毒物的宝箱时,魔法师的HP会减少20%,但Money会增加20%。

请根据上述描述,给出相应类的设计并完整实现,要求你的设计应具有良好的扩展性,如增加新角色类型及箱子种类时,不需要修改已有的设计及实现。

设计方案

class Role {
protected:
    int hp;
    int money;
public:
    virtual void PoisonHurt() = 0;
    virtual void AddMoney(int m) { money += m; }
    int GetMoney() { return money; }
    void SetMoney(int m) { money = m; }
};

class Soldier : public Role {
public:
    void PoisonHurt() override {
        hp *= 0.9;
        money *= 1.4;
    }
};

class Mage : public Role {
public:
    void PoisonHurt() override {
        hp *= 0.8;
        money *= 1.2;
    }
};

class Box {
public:
    virtual ~Box() {}
    virtual void BeOpened(Role &role) = 0;
};

class MoneyBox : public Box {
private:
    int boxMoney;
public:
    MoneyBox(int m) : boxMoney(m) {}
    void BeOpened(Role &role) override {
        role.SetMoney(role.GetMoney() + boxMoney);
        boxMoney = 0;
    }
    int GetBoxMoney() { return boxMoney; }
};

class PoisonBox : public Box {
public:
    void BeOpened(Role &role) override {
        role.PoisonHurt();
    }
};

第四题

给出适当的类设计和相应的代码: 有一个只能放进不能取出的盒子, 最多可放8个水果, 不一定一天放入。水果只是苹果和桔子两种, 它们放入盒子前的原始重量分别为50和30, 放入盒子后, 由于丢失水分, 它们的重量减轻, 苹果和桔子每天分别减轻4和3, 直到达到各自原始重量的3/5后, 不再减轻重量。

盒子的功能有:

  • 输出盒子中苹果的数量
  • 输出盒子中桔子的数量
  • 输出一天来盒子中水果减轻的总重量
  • 输出当前水果的总重量

设计方案

class Fruit {
protected:
    int mMaxWeight;
    int mMinWeight;
    int mLoseWeight;
    int mWeight;
public:
    Fruit(int mMax = 0, int mMin = 0, int mLose = 0, int mW = 0)
        : mMaxWeight(mMax), mMinWeight(mMin), mLoseWeight(mLose), mWeight(mW) {}
    
    virtual int ReduceWeight();
    virtual int Weight() const;
    virtual Fruit* Clone() = 0;
};

int Fruit::ReduceWeight() {
    int newWeight = mWeight - mLoseWeight;
    if (newWeight < mMinWeight)
        newWeight = mMinWeight;
    int reduce = mWeight - newWeight;
    mWeight = newWeight;
    return reduce;
}

int Fruit::Weight() const {
    return mWeight;
}

class Apple : public Fruit {
public:
    Apple() : Fruit(50, 50 * 3 / 5, 4, 50) {}
    Fruit* Clone() override {
        return new Apple(*this);
    }
};

class Orange : public Fruit {
public:
    Orange() : Fruit(30, 30 * 3 / 5, 3, 30) {}
    Fruit* Clone() override {
        return new Orange(*this);
    }
};

class Box {
private:
    Fruit* fruit[8];
    int count;
public:
    Box() : count(0) {
        for (int i = 0; i < 8; i++)
            fruit[i] = nullptr;
    }
    
    ~Box() {
        for (int i = 0; i < 8; i++)
            delete fruit[i];
    }
    
    void AddFruit(Fruit &one) {
        if (count < 8) {
            fruit[count++] = one.Clone();
        }
    }
    
    int ApplesNum() const {
        int num = 0;
        for (int i = 0; i < 8; i++) {
            Apple* p = dynamic_cast<Apple*>(fruit[i]);
            if (p)
                num++;
        }
        return num;
    }
    
    int OrangesNum() const {
        int num = 0;
        for (int i = 0; i < 8; i++) {
            Orange* p = dynamic_cast<Orange*>(fruit[i]);
            if (p)
                num++;
        }
        return num;
    }
    
    int PassOneDay() {
        int totalReduce = 0;
        for (int i = 0; i < 8; i++) {
            if (fruit[i])
                totalReduce += fruit[i]->ReduceWeight();
        }
        return totalReduce;
    }
    
    int TotalWeight() const {
        int total = 0;
        for (int i = 0; i < 8; i++) {
            if (fruit[i])
                total += fruit[i]->Weight();
        }
        return total;
    }
    
    void Show() {
        cout << "苹果数量: " << ApplesNum() << endl;
        cout << "桔子数量: " << OrangesNum() << endl;
        cout << "当前总重量: " << TotalWeight() << endl;
    }
};

int main() {
    Box aBox;
    Apple a1, a2, a3;
    Orange o1, o2;
    
    cout << "初始状态:" << endl;
    aBox.Show();
    
    aBox.AddFruit(a1);
    cout << "\n添加一个苹果后:" << endl;
    aBox.Show();
    
    aBox.AddFruit(a2);
    cout << "\n添加第二个苹果后:" << endl;
    aBox.Show();
    
    aBox.AddFruit(o1);
    aBox.AddFruit(o2);
    cout << "\n添加两个桔子后:" << endl;
    aBox.Show();
    
    int reduce = aBox.PassOneDay();
    cout << "\n一天后:" << endl;
    cout << "总减轻重量: " << reduce << endl;
    aBox.Show();
    
    return 0;
}

作业二

第一题

小王正在编写一个简单的计算器程序,要求输入两个整数和运算符号(加、减、乘、除),输出计算结果。小王用面向过程方法编写了下面的代码。请采用面向对象方法通过恰当的设计模式对小王的代码进行重构。请写出你所选择的设计模式,画出类图,并给出核心代码。

设计模式:简单工厂模式

简单工厂模式类图

核心代码

abstract class Operation {
    private int numberA;
    private int numberB;
    
    public abstract double getResult();
    
    public int getNumberA() {
        return numberA;
    }
    
    public void setNumberA(int numberA) {
        this.numberA = numberA;
    }
    
    public int getNumberB() {
        return numberB;
    }
    
    public void setNumberB(int numberB) {
        this.numberB = numberB;
    }
}

class AddOperation extends Operation {
    @Override
    public double getResult() {
        return numberA + numberB;
    }
}

class SubOperation extends Operation {
    @Override
    public double getResult() {
        return numberA - numberB;
    }
}

class MulOperation extends Operation {
    @Override
    public double getResult() {
        return numberA * numberB;
    }
}

class DivOperation extends Operation {
    @Override
    public double getResult() {
        return numberA / (double) numberB;
    }
}

class Calculator {
    public static Operation createOperation(char operator) {
        Operation op = null;
        switch (operator) {
            case '+':
                op = new AddOperation();
                break;
            case '-':
                op = new SubOperation();
                break;
            case '*':
                op = new MulOperation();
                break;
            case '/':
                op = new DivOperation();
                break;
            default:
                System.out.println("非法操作符");
        }
        return op;
    }
}

public class Client {
    public static void main(String[] args) {
        double result;
        Operation op1 = Calculator.createOperation('+');
        op1.setNumberA(5);
        op1.setNumberB(10);
        result = op1.getResult();
        System.out.println(result);
    }
}

第二题

请为一校服制造厂编写一个校服生产子系统。该工厂可为多家学校生产校服,包括秋季校服和夏季校服。一套秋季校服含一件长袖上衣和一条秋季长裤,一套夏季校服含一件短袖衬衣、一件短袖T恤、一条夏季长裤和一条短裤。不同学校校服款式不同。

请设计一个子系统为三所学校(一中、二中、三中)分别生产秋季校服和夏季校服(均码)。例如,当用户输入"一中+夏季"时,系统就会生产出一套一中夏季校服;当用户输入"一中+秋季"时,系统生产出一套一中秋季校服。

请写出你所选择的设计模式,画出类图,并给出核心代码。

设计模式:抽象工厂模式

抽象工厂模式类图

核心代码

interface AbstractFactory {
    public abstract SummerCloth createSummerCloth();
    public abstract FallCloth createFallCloth();
}

class FactoryFirst implements AbstractFactory {
    @Override
    public SummerCloth createSummerCloth() {
        return new SummerClothFirst();
    }
    
    @Override
    public FallCloth createFallCloth() {
        return new FallClothFirst();
    }
}

class FactorySecond implements AbstractFactory {
    @Override
    public SummerCloth createSummerCloth() {
        return new SummerClothSecond();
    }
    
    @Override
    public FallCloth createFallCloth() {
        return new FallClothSecond();
    }
}

class FactoryThird implements AbstractFactory {
    @Override
    public SummerCloth createSummerCloth() {
        return new SummerClothThird();
    }
    
    @Override
    public FallCloth createFallCloth() {
        return new FallClothThird();
    }
}

interface SummerCloth {
    public abstract void display();
}

class SummerClothFirst implements SummerCloth {
    @Override
    public void display() {
        System.out.println("一中夏季校服");
    }
}

class SummerClothSecond implements SummerCloth {
    @Override
    public void display() {
        System.out.println("二中夏季校服");
    }
}

class SummerClothThird implements SummerCloth {
    @Override
    public void display() {
        System.out.println("三中夏季校服");
    }
}

interface FallCloth {
    public abstract void display();
}

class FallClothFirst implements FallCloth {
    @Override
    public void display() {
        System.out.println("一中秋季校服");
    }
}

class FallClothSecond implements FallCloth {
    @Override
    public void display() {
        System.out.println("二中秋季校服");
    }
}

class FallClothThird implements FallCloth {
    @Override
    public void display() {
        System.out.println("三中秋季校服");
    }
}

public class Client {
    public static void main(String[] args) {
        AbstractFactory f1, f2, f3;
        f1 = new FactoryFirst();
        f2 = new FactorySecond();
        f3 = new FactoryThird();
        
        SummerCloth c1, c2;
        FallCloth c3;
        
        c1 = f1.createSummerCloth();
        c1.display();
        
        c2 = f2.createSummerCloth();
        c2.display();
        
        c3 = f3.createFallCloth();
        c3.display();
    }
}

第三题

小王正在设计一个导出数据的应用框架。客户要求:导出数据可能存储成不同的文件格式,例如:文本格式、数据库备份形式、Excel格式、Xml格式等等,并且,不管什么格式,导出数据文件都分成三个部分,分别是文件头、文件体和文件尾。

在文件头部分,需要描述如下信息:分公司或门市点编号、导出数据的日期,对于文本格式,中间用逗号分隔。

在文件体部分,需要描述如下信息:表名称、然后分条描述数据,对于文本格式,表名称单独占一行,数据描述一行算一条数据,字段间用逗号分隔。

在文件尾部分,需要描述如下信息:输出人。

请写出你所选择的设计模式,画出类图,并给出核心代码。

设计模式:建造者模式

建造者模式类图

核心代码

class File {
    private String head;
    private String body;
    private String tail;
    
    public String getHead() {
        return head;
    }
    
    public void setHead(String head) {
        this.head = head;
    }
    
    public String getBody() {
        return body;
    }
    
    public void setBody(String body) {
        this.body = body;
    }
    
    public String getTail() {
        return tail;
    }
    
    public void setTail(String tail) {
        this.tail = tail;
    }
}

abstract class Builder {
    protected File file = new File();
    
    public File getFile() {
        return file;
    }
    
    public abstract void buildHead();
    public abstract void buildBody();
    public abstract void buildTail();
}

class XmlBuilder extends Builder {
    @Override
    public void buildHead() {
        file.setHead("xml文件头");
    }
    
    @Override
    public void buildBody() {
        file.setBody("xml文件体");
    }
    
    @Override
    public void buildTail() {
        file.setTail("xml文件尾");
    }
}

class TextBuilder extends Builder {
    @Override
    public void buildHead() {
        file.setHead("文本文件头");
    }
    
    @Override
    public void buildBody() {
        file.setBody("文本文件体");
    }
    
    @Override
    public void buildTail() {
        file.setTail("文本文件尾");
    }
}

class DBBuilder extends Builder {
    @Override
    public void buildHead() {
        file.setHead("数据库备份文件头");
    }
    
    @Override
    public void buildBody() {
        file.setBody("数据库备份文件体");
    }
    
    @Override
    public void buildTail() {
        file.setTail("数据库备份文件尾");
    }
}

class ExcelBuilder extends Builder {
    @Override
    public void buildHead() {
        file.setHead("Excel文件头");
    }
    
    @Override
    public void buildBody() {
        file.setBody("Excel文件体");
    }
    
    @Override
    public void buildTail() {
        file.setTail("Excel文件尾");
    }
}

class Director {
    private Builder builder;
    
    public Director(Builder builder) {
        this.builder = builder;
    }
    
    public File construct() {
        builder.buildHead();
        builder.buildBody();
        builder.buildTail();
        return builder.getFile();
    }
}

public class Client {
    public static void main(String[] args) {
        Director director = new Director(new XmlBuilder());
        File file = director.construct();
        
        System.out.println("xml格式组成:");
        System.out.println(file.getHead());
        System.out.println(file.getBody());
        System.out.println(file.getTail());
    }
}

第四题

在下面的TicketMaker类(见下表)中,每次调用getNextTicketNumber方法都会返回1000,1001,1002…的数列。我们可以用它生成票的编号或是其他序列号。在现在该类的实现方式下,我们可以生成多个该类的实例。

请写出你所选择的设计模式,画出类图,并给出核心代码。

设计模式:单例模式(双检锁)

单例模式类图

核心代码

public class TicketMaker {
    private static volatile TicketMaker instance;
    private int ticketNumber = 1000;
    
    private TicketMaker() {}
    
    public static TicketMaker getInstance() {
        if (instance == null) {
            synchronized (TicketMaker.class) {
                if (instance == null) {
                    instance = new TicketMaker();
                }
            }
        }
        return instance;
    }
    
    public synchronized int getNextTicketNumber() {
        return ticketNumber++;
    }
}

第五题

请设计一个数据库连接池,要求最多只能创建3个连接实例。

设计模式:个数限制的单例模式

import java.util.Random;

public class DBConnections {
    private static DBConnections[] connections = {
        new DBConnections(),
        new DBConnections(), 
        new DBConnections()
    };
    
    private DBConnections() {}
    
    public static DBConnections getInstance() {
        return connections[random()];
    }
    
    public static int random() {
        Random random = new Random();
        int value = Math.abs(random.nextInt());
        value %= 3;
        return value;
    }
    
    public void ConnectionInfo() {
        // 连接信息
    }
    
    public static void main(String[] args) {
        DBConnections db1, db2, db3;
        db1 = DBConnections.getInstance();
        db2 = DBConnections.getInstance();
        db3 = DBConnections.getInstance();
        
        System.out.println(db1 == db2);
        System.out.println(db1 == db3);
    }
}

作业三

第一题

请根据下面的对象适配器类图画出与之相对应的类适配器类图。

对象适配器类图

类适配器类图

类适配器类图


第二题

开发一个计算机操作系统的线程调度程序,要求实现时间片调度和抢占调度这2种调度算法,支持Windows、Unix和Linux这3个操作系统,并且将来有可能会增加新的调度算法和支持新的操作系统,请选择恰当的设计模式解决该问题,画出类图,写出关键代码。

设计模式:策略模式 + 抽象工厂模式

线程调度类图

核心代码

// 调度算法接口
interface SchedulingAlgorithm {
    void schedule();
}

// 时间片调度
class TimeSliceScheduling implements SchedulingAlgorithm {
    @Override
    public void schedule() {
        System.out.println("时间片调度算法");
    }
}

// 抢占调度
class PreemptiveScheduling implements SchedulingAlgorithm {
    @Override
    public void schedule() {
        System.out.println("抢占调度算法");
    }
}

// 操作系统抽象类
abstract class OperatingSystem {
    protected SchedulingAlgorithm schedulingAlgorithm;
    
    public OperatingSystem(SchedulingAlgorithm algorithm) {
        this.schedulingAlgorithm = algorithm;
    }
    
    public abstract void executeScheduling();
}

// Windows系统
class WindowsOS extends OperatingSystem {
    public WindowsOS(SchedulingAlgorithm algorithm) {
        super(algorithm);
    }
    
    @Override
    public void executeScheduling() {
        System.out.println("Windows系统:");
        schedulingAlgorithm.schedule();
    }
}

// Unix系统
class UnixOS extends OperatingSystem {
    public UnixOS(SchedulingAlgorithm algorithm) {
        super(algorithm);
    }
    
    @Override
    public void executeScheduling() {
        System.out.println("Unix系统:");
        schedulingAlgorithm.schedule();
    }
}

// Linux系统
class LinuxOS extends OperatingSystem {
    public LinuxOS(SchedulingAlgorithm algorithm) {
        super(algorithm);
    }
    
    @Override
    public void executeScheduling() {
        System.out.println("Linux系统:");
        schedulingAlgorithm.schedule();
    }
}

// 操作系统工厂
class OSFactory {
    public static OperatingSystem createOS(String type, SchedulingAlgorithm algorithm) {
        switch (type) {
            case "Windows":
                return new WindowsOS(algorithm);
            case "Unix":
                return new UnixOS(algorithm);
            case "Linux":
                return new LinuxOS(algorithm);
            default:
                throw new IllegalArgumentException("不支持的操作系统类型");
        }
    }
}

public class Client {
    public static void main(String[] args) {
        SchedulingAlgorithm timeSlice = new TimeSliceScheduling();
        SchedulingAlgorithm preemptive = new PreemptiveScheduling();
        
        OperatingSystem windows = OSFactory.createOS("Windows", timeSlice);
        OperatingSystem linux = OSFactory.createOS("Linux", preemptive);
        
        windows.executeScheduling();
        linux.executeScheduling();
    }
}

作业四

第一题

请设计一个图形编辑器,支持绘制多种图形(圆形、矩形、三角形等),并且支持撤销、重做操作。

设计模式:命令模式 + 工厂模式

图形编辑器类图

核心代码

import java.util.Stack;

// 图形接口
interface Shape {
    void draw();
}

// 圆形
class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("绘制圆形");
    }
}

// 矩形
class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("绘制矩形");
    }
}

// 三角形
class Triangle implements Shape {
    @Override
    public void draw() {
        System.out.println("绘制三角形");
    }
}

// 图形工厂
class ShapeFactory {
    public static Shape createShape(String type) {
        switch (type) {
            case "circle":
                return new Circle();
            case "rectangle":
                return new Rectangle();
            case "triangle":
                return new Triangle();
            default:
                throw new IllegalArgumentException("不支持的图形类型");
        }
    }
}

// 命令接口
interface Command {
    void execute();
    void undo();
}

// 绘制命令
class DrawCommand implements Command {
    private Shape shape;
    
    public DrawCommand(Shape shape) {
        this.shape = shape;
    }
    
    @Override
    public void execute() {
        shape.draw();
    }
    
    @Override
    public void undo() {
        System.out.println("撤销绘制");
    }
}

// 命令执行者
class Invoker {
    private Stack<Command> undoStack = new Stack<>();
    private Stack<Command> redoStack = new Stack<>();
    
    public void executeCommand(Command command) {
        command.execute();
        undoStack.push(command);
        redoStack.clear();
    }
    
    public void undo() {
        if (!undoStack.isEmpty()) {
            Command command = undoStack.pop();
            command.undo();
            redoStack.push(command);
        }
    }
    
    public void redo() {
        if (!redoStack.isEmpty()) {
            Command command = redoStack.pop();
            command.execute();
            undoStack.push(command);
        }
    }
}

// 编辑器
class GraphicsEditor {
    private Invoker invoker = new Invoker();
    
    public void drawShape(String shapeType) {
        Shape shape = ShapeFactory.createShape(shapeType);
        Command command = new DrawCommand(shape);
        invoker.executeCommand(command);
    }
    
    public void undo() {
        invoker.undo();
    }
    
    public void redo() {
        invoker.redo();
    }
}

public class Client {
    public static void main(String[] args) {
        GraphicsEditor editor = new GraphicsEditor();
        
        editor.drawShape("circle");
        editor.drawShape("rectangle");
        
        editor.undo();
        editor.redo();
    }
}

历年真题

真题一

请设计一个日志系统,支持多种日志输出方式(控制台、文件、数据库等),并且支持不同的日志级别(DEBUG、INFO、WARN、ERROR)。

设计模式:观察者模式 + 策略模式

日志系统类图

核心代码

import java.util.ArrayList;
import java.util.List;

// 日志级别
enum LogLevel {
    DEBUG, INFO, WARN, ERROR
}

// 日志事件
class LogEvent {
    private String message;
    private LogLevel level;
    private long timestamp;
    
    public LogEvent(String message, LogLevel level) {
        this.message = message;
        this.level = level;
        this.timestamp = System.currentTimeMillis();
    }
    
    // getters
    public String getMessage() { return message; }
    public LogLevel getLevel() { return level; }
    public long getTimestamp() { return timestamp; }
}

// 日志观察者接口
interface LogObserver {
    void log(LogEvent event);
}

// 控制台输出
class ConsoleLogger implements LogObserver {
    @Override
    public void log(LogEvent event) {
        System.out.println("[" + event.getLevel() + "] " + event.getMessage());
    }
}

// 文件输出
class FileLogger implements LogObserver {
    @Override
    public void log(LogEvent event) {
        // 写入文件
        System.out.println("File: [" + event.getLevel() + "] " + event.getMessage());
    }
}

// 数据库输出
class DatabaseLogger implements LogObserver {
    @Override
    public void log(LogEvent event) {
        // 写入数据库
        System.out.println("DB: [" + event.getLevel() + "] " + event.getMessage());
    }
}

// 日志主题
class LoggerSubject {
    private List<LogObserver> observers = new ArrayList<>();
    private LogLevel level;
    
    public void setLevel(LogLevel level) {
        this.level = level;
    }
    
    public void addObserver(LogObserver observer) {
        observers.add(observer);
    }
    
    public void removeObserver(LogObserver observer) {
        observers.remove(observer);
    }
    
    public void log(LogLevel level, String message) {
        if (level.ordinal() >= this.level.ordinal()) {
            LogEvent event = new LogEvent(message, level);
            notifyObservers(event);
        }
    }
    
    private void notifyObservers(LogEvent event) {
        for (LogObserver observer : observers) {
            observer.log(event);
        }
    }
    
    // 便捷方法
    public void debug(String message) { log(LogLevel.DEBUG, message); }
    public void info(String message) { log(LogLevel.INFO, message); }
    public void warn(String message) { log(LogLevel.WARN, message); }
    public void error(String message) { log(LogLevel.ERROR, message); }
}

public class Client {
    public static void main(String[] args) {
        LoggerSubject logger = new LoggerSubject();
        logger.setLevel(LogLevel.INFO);
        
        logger.addObserver(new ConsoleLogger());
        logger.addObserver(new FileLogger());
        
        logger.debug("调试信息"); // 不会输出
        logger.info("普通信息");
        logger.error("错误信息");
    }
}

真题二

请设计一个缓存系统,支持LRU(最近最少使用)淘汰策略,并且支持多种缓存存储方式(内存、磁盘、分布式等)。

设计模式:策略模式 + 装饰器模式

缓存系统类图

核心代码

import java.util.LinkedHashMap;
import java.util.Map;

// 缓存存储接口
interface CacheStorage {
    Object get(String key);
    void put(String key, Object value);
    void remove(String key);
    boolean containsKey(String key);
}

// 内存缓存
class MemoryCache implements CacheStorage {
    private Map<String, Object> cache = new LinkedHashMap<>();
    
    @Override
    public Object get(String key) {
        return cache.get(key);
    }
    
    @Override
    public void put(String key, Object value) {
        cache.put(key, value);
    }
    
    @Override
    public void remove(String key) {
        cache.remove(key);
    }
    
    @Override
    public boolean containsKey(String key) {
        return cache.containsKey(key);
    }
}

// 磁盘缓存
class DiskCache implements CacheStorage {
    @Override
    public Object get(String key) {
        // 从磁盘读取
        return null;
    }
    
    @Override
    public void put(String key, Object value) {
        // 写入磁盘
    }
    
    @Override
    public void remove(String key) {
        // 从磁盘删除
    }
    
    @Override
    public boolean containsKey(String key) {
        // 检查磁盘
        return false;
    }
}

// LRU缓存装饰器
class LRUCacheDecorator implements CacheStorage {
    private CacheStorage cacheStorage;
    private LinkedHashMap<String, Object> lruMap;
    private int capacity;
    
    public LRUCacheDecorator(CacheStorage cacheStorage, int capacity) {
        this.cacheStorage = cacheStorage;
        this.capacity = capacity;
        this.lruMap = new LinkedHashMap<String, Object>(capacity, 0.75f, true) {
            @Override
            protected boolean removeEldestEntry(Map.Entry<String, Object> eldest) {
                if (size() > capacity) {
                    cacheStorage.remove(eldest.getKey());
                    return true;
                }
                return false;
            }
        };
    }
    
    @Override
    public Object get(String key) {
        Object value = cacheStorage.get(key);
        if (value != null) {
            lruMap.get(key); // 更新访问顺序
        }
        return value;
    }
    
    @Override
    public void put(String key, Object value) {
        cacheStorage.put(key, value);
        lruMap.put(key, value);
    }
    
    @Override
    public void remove(String key) {
        cacheStorage.remove(key);
        lruMap.remove(key);
    }
    
    @Override
    public boolean containsKey(String key) {
        return cacheStorage.containsKey(key);
    }
}

// 缓存管理器
class CacheManager {
    private CacheStorage cache;
    
    public CacheManager(CacheStorage cache) {
        this.cache = cache;
    }
    
    public Object get(String key) {
        return cache.get(key);
    }
    
    public void put(String key, Object value) {
        cache.put(key, value);
    }
    
    public void remove(String key) {
        cache.remove(key);
    }
}

public class Client {
    public static void main(String[] args) {
        CacheStorage memoryCache = new MemoryCache();
        CacheStorage lruCache = new LRUCacheDecorator(memoryCache, 3);
        
        CacheManager cacheManager = new CacheManager(lruCache);
        
        cacheManager.put("key1", "value1");
        cacheManager.put("key2", "value2");
        cacheManager.put("key3", "value3");
        
        System.out.println(cacheManager.get("key1")); // value1
        
        cacheManager.put("key4", "value4"); // key2被淘汰
        System.out.println(cacheManager.get("key2")); // null
    }
}

总结

本指南涵盖了多种设计模式的应用实例,包括:

  • 创建型模式:工厂模式、抽象工厂模式、建造者模式、单例模式
  • 结构型模式:适配器模式、装饰器模式
  • 行为型模式:策略模式、命令模式、观察者模式

通过这些作业和真题的练习,可以帮助你深入理解设计模式的原理和应用场景,提高面向对象设计能力。

About

设计模式学习指南,包含多个作业和真题练习。

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published