文章目录
前言
本篇博客截取的是**《C#设计模式(第2版)》这本书课后习题里面的各个设计模式的典型习题并完成 java代码实现,每个代码实现都有相应的业务需求背景可以帮助更好的理解设计模式,其中包括的设计模式包括 外观模式、享元模式、职责链模式、命令模式、备忘录模式、迭代器模式、观察者模式以及状态模式**。后续会继续出其他设计模式的续集并就一些应用广泛的设计模式进行深入剖析。
外观模式
T1. P188 T5 外观模式
在计算机主机(Mainframe)中,只需按下主机的开机按钮(On()),即可调用其他硬件设备和软件的启动方法,如内存(Memory)的自检(Check())、CPU的运行(Run())、硬盘(HardDisk)的读取(Read())、操作系统(OS)的载入(Load())等,如果某一过程发生错误则计算机启动失败。试使用外观模式模拟该过程。
bash
//外观角色Mainframe
class Mainframe{
//维持对其他对象的引用
private Memory memory;
private CPU cpu;
private HardDisk hardDisk;
private OS os;
public Mainframe(){
memory=new Memory();
cpu=new CPU();
hardDisk=new HardDisk();
os=new OS();
}
//调用其他对象的业务方法
public void on(){
memory.check();
cpu.run();
hardDisk.read();
os.load();
}
}
//子系统角色Memory
class Memory{
void check(){
System.out.println("内存启动自检...");
}
}
//子系统角色CPU
class CPU{
void run(){
System.out.println("CPU启动运行...");
}
}
//子系统角色HardDisk
class HardDisk{
void read(){
System.out.println("硬盘启动读取...");
}
}
//子系统角色OS
class OS{
void load(){
System.out.println("操作系统启动载入...");
}
}
//测试
public class MainframeTest {
public static void main(String[] args) {
//针对抽象外观编程
Mainframe mainframe=new Mainframe();
mainframe.on();
}
}
享元模式
T2. P204 T5 享元模式
在屏幕中显示一个文本文档,其中相同的字符串"CSharp"共享同一个对象,而这些字符串的颜色和大小可以不同。现使用享元模式设计一个方案实现字符串对象的共享,要求绘制类图并使用编程实现。
bash
//Color类充当外部状态类
class Color{
private String color;
public Color(String color) {
this.color = color;
}
public void setColor(String color){
this.color=color;
}
public String getColor(){
return this.color;
}
}
//Size充当外部状态类
class Size{
private int value;
public Size(int value){
this.value=value;
}
public void setValue(int value){
this.value=value;
}
public int getValue(){
return this.value;
}
}
//抽象共享字符串,担任抽象享元角色
abstract class AbstractSharedString{
String content;
public abstract String GetContent();
public abstract void Display(Color color,Size size);
}
//共享字符串CSharp,担任具体享元角色
class SharedString extends AbstractSharedString{
public SharedString(String content){
this.content=content;
}
public String GetContent(){
return content;
}
public void Display(Color color,Size size){
System.out.println("内容: "+GetContent()+",颜色:"+color.getColor()+",大小:"+size.getValue());
}
}
//SharedStringFactory充当享元工厂角色
class SharedStringFactory{
private Map <String,Object>mp;
public SharedStringFactory(){
mp=new HashMap<>();
}
public AbstractSharedString getSharedString(String arg){
if (!mp.containsKey(arg)) {
AbstractSharedString str = new SharedString(arg);
mp.put(arg, str);
}
return (AbstractSharedString)mp.get(arg);
}
}
//客户类中测试显示两个颜色和大小不同的字符串"CSharp"
public class SharedStringTest {
public static void main(String[] args) {
AbstractSharedString str1,str2;
SharedStringFactory factory=new SharedStringFactory();
//红色5号字体
str1=factory.getSharedString("CSharp");
str1.Display(new Color("红色"),new Size(5));
//黑色10号字体
str2=factory.getSharedString("CSharp");
str2.Display(new Color("黑色"),new Size(10));
System.out.println(str1==str2);//输出true
}
}
职责链模式
T3. P234 T4 职责链模式
客户类模拟情报人员,首先向级别最低的班长递交任务书,即军情,如果超出
班长的权力范围,则传递给排长,排长如果也不能处理则传递给营长,如果营长也不能处理则需要开会讨论。
bash
//Mission充当请求角色
class Mission{
String code;
int enemyNumber;
public Mission(String code, int enemyNumber) {
this.code = code;
this.enemyNumber = enemyNumber;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public int getEnemyNumber() {
return enemyNumber;
}
public void setEnemyNumber(int enemyNumber) {
this.enemyNumber = enemyNumber;
}
}
//Officer充当抽象传递者角色
abstract class Officer{
protected String name;
protected Officer successor;
public Officer(String name){
this.name=name;
}
public void setSuccessor(Officer successor){
this.successor=successor;
}
public abstract void handleRequest(Mission request);
}
//班长monitor充当具体传递者角色
class Monitor extends Officer{
public Monitor(String name){
super(name);
}
public void handleRequest(Mission request){
if(request.getEnemyNumber()<10){
System.out.println("班长" + name + "下达代号为" + request.getCode() + "的作 战任务,敌人数量为" + request.getEnemyNumber());
}else{
if(this.successor!=null){
this.successor.handleRequest(request);
}
}
}
}
//排长Platoon充当具体传递者角色
class Platoon extends Officer{
public Platoon(String name){
super(name);
}
public void handleRequest(Mission request){
if(request.getEnemyNumber()>=10&&request.getEnemyNumber()<50){
System.out.println("排长" + name + "下达代号为" + request.getCode() + "的作战任务,敌人数量为" + request.getEnemyNumber());
}else{
if(this.successor!=null){
this.successor.handleRequest(request);
}
}
}
}
//营长BattalionCommander充当具体传递者角色
class BattalionCommander extends Officer{
public BattalionCommander(String name){
super(name);
}
public void handleRequest(Mission request){
if(request.getEnemyNumber()<200){
System.out.println("营长" + name + "下达代号为" + request.getCode() + "的作战任务,敌人数量为" + request.getEnemyNumber());
}else{
System.out.println("敌人数量过多,共有人数:"+request.getEnemyNumber()+",所以需要共同讨论代号为"+request.getCode()+"的作战任务");
}
}
}
public class MissionTest {
public static void main(String[] args) {
Officer officer1,officer2,officer3;
//班长张三
officer1=new Monitor("张三");
//排长李四
officer2=new Platoon("李四");
//营长王五
officer3=new BattalionCommander("王五");
//创建职责链
officer1.setSuccessor(officer2);
officer2.setSuccessor(officer3);
//作战四个不同代号不同敌人数量的任务
Mission mission1,mission2,mission3,mission4;
mission1=new Mission("001",5);
mission2=new Mission("010",20);
mission3=new Mission("100",100);
mission4=new Mission("101",500);
//发送请求,请求对象通常为自定义类型
officer1.handleRequest(mission1);
officer1.handleRequest(mission2);
officer1.handleRequest(mission3);
officer1.handleRequest(mission4);
}
}
命令模式
T4. P252 T3 命令模式
房间中的开关就是命令模式的一种实现,试使用命令模式来模拟开关的功能,可控制对象包括电灯和电风扇
bash
//Command充当抽象命令类
abstract class Command{
public abstract void executeOn();
public abstract void executeOff();
}
//switch充当调用者(发送者)角色
class Switch{
//维持一个抽象命令对象的引用
private Command command;
public Command getCommand() {
return command;
}
public void setCommand(Command command) {
this.command = command;
}
//发送请求的方法
public void Click(){
command.executeOn();
command.executeOff();
}
}
//LampCommand充当具体命令角色
class LampCommand extends Command{
//维持对请求接收者的引用
private Lamp lamp;
public LampCommand(){
lamp=new Lamp();
}
@Override
public void executeOn() {
lamp.lighten();
}
@Override
public void executeOff() {
lamp.quench();
}
}
//FanCommand充当具体命令类
class FanCommand extends Command{
//维持对请求接收者的引用
private Fan fan;
public FanCommand(){
fan=new Fan();
}
@Override
public void executeOn() {
fan.run();
}
@Override
public void executeOff() {
fan.stop();
}
}
//fan充当接受者命令
class Fan{
public void run(){
System.out.println("开电风扇");
}
public void stop(){
System.out.println("关电风扇");
}
}
//Lamp充当接收者角色
class Lamp{
public void lighten(){
System.out.println("开电灯");
}
public void quench(){
System.out.println("关电灯");
}
}
public class SwitchTest {
public static void main(String[] args) {
Switch sw=new Switch();
//定义命令对象
Command commandLamp,commandFan;
commandLamp=new LampCommand();
commandFan=new FanCommand();
//将电灯命令对象注入开关
sw.setCommand(commandLamp);
sw.Click();
//将电风扇命令对象注入开关
sw.setCommand(commandFan);
sw.Click();
}
}
命令模式2
- P252 T5(按题目要求完成)(命令模式)
设计并实现一个简单的请求日志记录程序,将一组命令对象通过序列化写到
日志文件中,并通过日志文件实现批处理操作
bash
import java.io.*;
import java.util.ArrayList;
//配置文件设置窗口类,充当请求发送者
class ConfigSettingWindow {
//定义一个集合来存储每一次操作时的命令对象
private ArrayList<Command> commands = new ArrayList<Command>();
private Command command;
//注入具体命令对象
public void setCommand(Command command) {
this.command = command;
}
//执行配置文件修改命令,同时将命令对象添加到命令集合中
public void call(String args) {
command.execute(args);
commands.add(command);
}
//记录请求日志,生成日志文件,将命令集合写入日志文件
public void save() {
FileUtil.writeCommands(commands);
}
//从日志文件中提取命令集合,并循环调用每一个命令对象的execute()方法来实现配置文件的重新设置
public void recover() {
ArrayList list;
list = FileUtil.readCommands();
for (Object obj : list) {
((Command)obj).execute();
}
}
}
//抽象命令类,由于需要将命令对象写入文件,因此它实现了Serializable接口
abstract class Command implements Serializable {
//维持对接收者对象的引用
protected ConfigOperator configOperator;
protected String name; //命令名称
protected String args; //命令参数
public Command(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public void setConfigOperator(ConfigOperator configOperator) {
this.configOperator = configOperator;
}
//声明两个抽象的执行方法execute()
public abstract void execute(String args);
public abstract void execute();
}
//增加命令类:具体命令类
class InsertCommand extends Command {
public InsertCommand(String name) {
super(name);
}
public void execute(String args) {
this.args = args;
configOperator.insert(args);
}
public void execute() {
configOperator.insert(this.args);
}
}
//修改命令类:具体命令类
class ModifyCommand extends Command {
public ModifyCommand(String name) {
super(name);
}
public void execute(String args) {
this.args = args;
configOperator.modify(args);
}
public void execute() {
configOperator.modify(this.args);
}
}
//删除命令类:具体命令类
class DeleteCommand extends Command {
public DeleteCommand(String name) {
super(name);
}
public void execute(String args) {
this.args = args;
configOperator.delete(args);
}
public void execute() {
configOperator.delete(this.args);
}
}
//配置文件操作类:请求接收者。由于ConfigOperator类的对象是Command的成员对象,它也将随Command对象一起写入文件,因此ConfigOperator也需要实现Serializable接口
class ConfigOperator implements Serializable {
public void insert(String args) {
System.out.println("增加新节点:" + args);
}
public void modify(String args) {
System.out.println("修改节点:" + args);
}
public void delete(String args) {
System.out.println("删除节点:" + args);
}
}
//工具类:文件操作类
class FileUtil {
//将命令集合写入日志文件
public static void writeCommands(ArrayList commands) {
try {
FileOutputStream file = new FileOutputStream("config.log");
//创建对象输出流用于将对象写入到文件中
ObjectOutputStream objout = new ObjectOutputStream(new BufferedOutputStream(file));
//将对象写入文件
objout.writeObject(commands);
objout.close();
} catch (Exception e) {
System.out.println("命令保存失败!");
e.printStackTrace();
}
}
//从日志文件中提取命令集合
public static ArrayList readCommands() {
try {
FileInputStream file = new FileInputStream("config.log");
//创建对象输入流用于从文件中读取对象
ObjectInputStream objin = new ObjectInputStream(new BufferedInputStream(file));
//将文件中的对象读出并转换为ArrayList类型
ArrayList commands = (ArrayList) objin.readObject();
objin.close();
return commands;
} catch (Exception e) {
System.out.println("命令读取失败!");
e.printStackTrace();
return null;
}
}
}
public class RequestLogClientTest {
public static void main(String[] args) {
ConfigSettingWindow csw = new ConfigSettingWindow(); //定义请求发送者
Command command; //定义命令对象
ConfigOperator co = new ConfigOperator(); //定义请求接收者
//四次对配置文件的更改
command = new InsertCommand("增加");
command.setConfigOperator(co);
csw.setCommand(command);
csw.call("网站首页");
command = new InsertCommand("增加");
command.setConfigOperator(co);
csw.setCommand(command);
csw.call("端口号");
command = new ModifyCommand("修改");
command.setConfigOperator(co);
csw.setCommand(command);
csw.call("网站首页");
command = new ModifyCommand("修改");
command.setConfigOperator(co);
csw.setCommand(command);
csw.call("端口号");
System.out.println("----------------------------");
System.out.println("保存配置");
csw.save();
System.out.println("----------------------------");
System.out.println("恢复配置");
System.out.println("----------------------------");
csw.recover();
}
}
命令模式3+备忘录模式
- P252 T5(在命令模式的基础上加上备忘录模式memento实现撤销操作)
设计并实现一个简单的请求日志记录程序,将一组命令对象通过序列化写到
日志文件中,并通过日志文件实现批处理操作
bash
import java.util.List;
public class RequestLogMementoTest {
public static void main(String[] args) {
CareTaker careTaker = new CareTaker();
LogFile logFile = new LogFile();
Command command = new RecordCommand(logFile);
OS os = new OS(command);
for (int i = 0; i < 10; i++) {
os.call("记录下了第" + (i + 1) + "条日志");
careTaker.addLogMemento(logFile.createMemento());
System.out.println(logFile.getLogs());
}
LogMemento logMementos = careTaker.getLogMementos(5);
logFile.restoreMemento(logMementos);
System.out.println(logFile.getLogs());
}
}
//请求发送者
class OS {
//维持对一个抽象命令对象的引用
private Command command;
public OS(Command command) {
this.command = command;
}
public Command getCommand() {
return command;
}
public void setCommand(Command command) {
this.command = command;
}
public void call(String log) {
command.execute(log);
}
}
//充当抽象命令类
abstract class Command {
public abstract void execute(String log);
}
//充当具体命令类
class RecordCommand extends Command {
//维持对请求接收者的引用
private LogFile logFile;
public RecordCommand(LogFile logFile) {
this.logFile = logFile;
}
//命令执行方法,将调用请求接收者的业务方法
@Override
public void execute(String log) {
logFile.record(log);
}
}
//请求接收者,同时充当备忘录模式中的原发器
class LogFile {
private List<String> logs = new ArrayList<>();
public List<String> getLogs() {
return logs;
}
public LogMemento createMemento() {
LogFile logFile=new LogFile();
List<String> l=new ArrayList<>();
l.addAll(logs);
logFile.setLogs(l);
return new LogMemento(logFile);
}
public void restoreMemento(LogMemento memento) {
this.logs = memento.getLogs();
}
public void setLogs(List<String> logs) {
this.logs = logs;
}
public void record(String log) {
logs.add(log);
}
}
//充当备忘录
class LogMemento {
private List<String> logs;
public LogMemento(LogFile logFile) {
logs = logFile.getLogs();
}
public List<String> getLogs() {
return logs;
}
public void setLogs(List<String> logs) {
this.logs = logs;
}
@Override
public String toString() {
return "LogMemento{" +
"logs=" + logs +
'}';
}
}
//负责人 保存备忘录
class CareTaker {
private List<LogMemento> logMementos = new ArrayList<>();
public LogMemento getLogMementos(int i) {
return logMementos.get(i);
}
public void addLogMemento(LogMemento logMemento) {
logMementos.add(logMemento);
}
}
迭代器模式
T7. P284 T4
电视机遥控器是一个迭代器的现实应用,通过它可以实现对电视频道集合的遍历,
可以将电视机看成一个存储频道的聚合对象,试模拟电视机遥控器的实现。
bash
import java.util.ArrayList;
import java.util.List;
//抽象聚合类
abstract class AbstractTelevisionList{
//频道列表
protected List<Object>objects=new ArrayList<Object>();
public AbstractTelevisionList(List<Object>objects){
this.objects=objects;
}
public void addObject(Object obj){
this.objects.add(obj);
}
public void removeObject(Object obj){
this.objects.remove(obj);
}
public List<Object> getObjects(){
return this.objects;
}
//声明创建迭代器对象的抽象工厂方法
public abstract AbstractIterator CreateIterator();
}
//具体聚合类,电视机作为存放频道的聚合对象
class TelevisionList extends AbstractTelevisionList{
public TelevisionList(List<Object>Televisions){
super(Televisions);
}
//实现创建迭代器对象的具体工厂方法
@Override
public AbstractIterator CreateIterator() {
return new RemoteControlIterator(this);
}
};
//抽象遥控器,充当抽象迭代器
interface AbstractIterator{
void next(); //移至下一个元素
boolean isLast(); //判断是否为最后一个元素
void previous(); //移至上一个元素
boolean isFirst(); //判断是否是第一个元素
Object getNextItem(); //获取下一个元素
Object getPreviousItem(); //获取上一个元素
}
//电视遥控器,充当具体迭代器
class RemoteControlIterator implements AbstractIterator{
private TelevisionList televisionList;
private List<Object> products;
private int cursor1; //定义一个游标,用于记录正向遍历的位置;
private int cursor2; //定义一个游标,用于记录逆向遍历的位置;
public RemoteControlIterator(TelevisionList list){
this.televisionList=list;
this.products=list.getObjects();//获取集合对象
cursor1=0; //设置正向遍历游标的初始值
cursor2=products.size()-1; //设置逆向遍历游标的初始值
}
@Override
public void next() {
if(cursor1<products.size()){
cursor1++;
}
}
@Override
public boolean isLast() {
return (cursor1==products.size());
}
@Override
public void previous() {
if(cursor2>-1){
cursor2--;
}
}
@Override
public boolean isFirst() {
return (cursor2==-1);
}
@Override
public Object getNextItem() {
return products.get(cursor1);
}
@Override
public Object getPreviousItem(){
return products.get(cursor2);
}
}
public class TelevisionRemoteControl {
public static void main(String[] args) {
List<Object> products=new ArrayList<>();
products.add("中央电视台");
products.add("浙江卫视");
products.add("湖南卫视");
products.add("江苏卫视");
products.add("东方卫视");
products.add("凤凰卫视");
AbstractTelevisionList list;
AbstractIterator iterator;
list=new TelevisionList(products); //创建聚合对象
iterator=list.CreateIterator(); //创建迭代器对象
System.out.println("正向遍历:");
while(!iterator.isLast()){
System.out.println(iterator.getNextItem()+",");
iterator.next();
}
System.out.println("----------------------------");
System.out.println("逆向遍历:");
while(!iterator.isFirst()){
System.out.println(iterator.getPreviousItem()+",");
iterator.previous();
}
}
}
观察者模式
T8.P332 T6
某实时在线股票软件需要提供以下功能:
当股票购买者所购买的某支股票价格变化幅度达到5%时,系统将自动发送通知(包括新价格)给购买该股票的所有股民。试使用观察者模式设计并实现该系统。
bash
//Investor充当抽象观察者
abstract class Investor{
public abstract void response(Stock stock);
}
//ConcreteInvestor充当具体观察者
class ConcreteInvestor extends Investor{
private String name;
public ConcreteInvestor(String name){
this.name=name;
}
public void response(Stock stock){
System.out.print("提醒股民:"+name);
System.out.print("----股票:"+stock.getStockName());
System.out.print("价格波动幅度超过5%-------");
System.out.println("新价格是:"+stock.getPrice()+".");
}
}
//Stack充当抽象目标类
abstract class Stock{
protected ArrayList<Investor> investors;
protected String stockName;
protected double price;
//买股票
public abstract void attach(Investor investor);
//退股票
public abstract void detach(Investor investor);
public String getStockName() {
return stockName;
}
public void setStockName(String stockName) {
this.stockName = stockName;
}
public double getPrice() {
return price;
}
public abstract void setPrice(double price);
public abstract void notifyObserver();
}
//ConcreteStock充当具体目标类
class ConcreteStock extends Stock{
public ConcreteStock(String stockName,double price) {
this.stockName=stockName;
this.price=price;
investors=new ArrayList<Investor>();
}
//买股票
@Override
public void attach(Investor investor){
investors.add(investor);
}
//退股票
@Override
public void detach(Investor investor){
investors.remove(investor);
}
//股票价格变化幅度达到5%时,自动通知所有购买该股票的股民
@Override
public void setPrice(double price) {
double range=Math.abs(price-this.price)/this.price;
this.price=price;
if(range>=0.05){
this.notifyObserver();
}
}
//遍历观察者集合,调用每一个股票购买者得response()方法
@Override
public void notifyObserver() {
for(Object obj:investors){
((Investor)obj).response(this);
}
}
}
public class StockTest {
public static void main(String[] args) {
//股民
Investor investor1,investor2,investor3;
investor1=new ConcreteInvestor("张三");
investor2=new ConcreteInvestor("李四");
investor3=new ConcreteInvestor("王五");
Stock stock1=new ConcreteStock("中芯国际",100.00);
stock1.attach(investor1);
stock1.attach(investor2);
stock1.attach(investor3);
stock1.setPrice(105.00);
}
}
状态模式
T9. P353 T4
传输门是传输系统中的重要装置。传输门具有Open(打开)、Closed(关闭)、Opening(正在打开)、StayOpen(保持打开)、Closing(正在关闭)5种状态。触发状态的转换事件有click、complete和timeout3种。事件与其相应的状态转换如图所示。试使用状态模式对传输门进行状态模拟,
bash
//door充当环境类
class Door {
public final DoorState CLOSED = new DoorClosed(this);
public final DoorState OPENING = new DoorOpening(this);
public final DoorState OPEN = new DoorOpen(this);
public final DoorState CLOSING = new DoorClosing(this);
public final DoorState STAYOPEN = new DoorStayOpen(this);
private DoorState state = CLOSED;
//设置传输门当前状态
public void setState(DoorState state){
this.state = state;
}
//根据当前状态输出对应的状态字符串
public void getState(){
System.out.println(state.getClass().getName());
}
//发生 click 事件时进行状态转换
public void click() {
state.click();
}
//发生 timeout 事件时进行状态转换
public void timeout() {
state.timeout();
}
//发生 complete 事件时进行状态转换
public void complete() {
state.complete();
}
}
//定义所有状态类的基类DoorState,DoorState充当抽象状态类角色
abstract class DoorState {
protected Door door ;
public DoorState(Door door) {
this.door = door;
}
public abstract void click();
public abstract void complete();
public abstract void timeout();
}
//DoorClosed充当具体状态类角色
class DoorClosed extends DoorState{
public DoorClosed(Door door) {
super(door);
}
public void click(){
door.setState(door.OPENING);
}
@Override
public void complete() {
door.setState(door.CLOSING);
}
@Override
public void timeout() {
}
}
//DoorOpening充当具体状态类角色
class DoorOpening extends DoorState{
public DoorOpening(Door door) {
super(door);
}
@Override
public void click() {
door.setState(door.CLOSING);
}
@Override
public void complete() {
door.setState(door.OPEN);
}
@Override
public void timeout() {
}
}
//DoorOpen充当具体状态类角色
class DoorOpen extends DoorState{
public DoorOpen(Door door) {
super(door);
}
@Override
public void click() {
door.setState(door.STAYOPEN);
}
@Override
public void complete() {
}
@Override
public void timeout() {
door.setState(door.CLOSING);
}
}
//DoorClosing充当具体状态类角色
class DoorClosing extends DoorState{
public DoorClosing(Door door) {
super(door);
}
@Override
public void click() {
door.setState(door.OPENING);
}
@Override
public void complete() {
door.setState(door.CLOSED);
}
@Override
public void timeout() {
}
}
//DoorStayOpen充当具体状态类角色
class DoorStayOpen extends DoorState{
public DoorStayOpen(Door door) {
super(door);
}
@Override
public void click() {
door.setState(door.CLOSING);
}
@Override
public void complete() {
}
@Override
public void timeout() {
}
}
public class TransmissionDoor {
public static void main(String[] args) {
Door aDoor = new Door();
aDoor.getState();
aDoor.click();
aDoor.getState();
aDoor.complete();
aDoor.getState();
aDoor.timeout();
aDoor.getState();
}
}