设计模式之原型模式(TypeScript & Rust)

原型模式(Prototype Pattern)是一种创建型设计模式,它通过复制现有对象来创建新对象,而不是使用常规的实例化过程。当对象创建成本高或创建过程很复杂时,通过复制一个现有的对象,然后基于复制出来的对象进行修改是一个非常好的方法。我们的学渣小明是使用原型模式的高手。期末考试了,学渣小明什么都不会,不慌,使用原型模式即可。

TypeScript

有一个试卷类 ExaminationPaper,包含名字 string、选择题 Question[] 和简答题 Question[] 三个属性:

ts 复制代码
interface Prototype {
  clone(): Prototype
}

class Question implements Prototype {
  private answer: string

  constructor(answer: string) {
    this.answer = answer
  }

  setAnswer(answer: string) {
    this.answer = answer
  }

  getAnswer(): string {
    return this.answer
  }

  clone(): Prototype {
    return new Question(this.answer)
  }
}

class ExaminationPaper implements Prototype {
  choiceQuestions: Question[]
  shortAnswerQuestions: Question[]
  name: string

  constructor(
    name: string,
    choiceQuestions: Question[],
    shortAnswerQuestions: Question[]
  ) {
    this.name = name
    this.choiceQuestions = choiceQuestions
    this.shortAnswerQuestions = shortAnswerQuestions
  }

  clone(): Prototype {
    return new ExaminationPaper(
      this.name,
      this.choiceQuestions.map((q) => q.clone() as Question),
      this.shortAnswerQuestions.map((q) => q.clone() as Question)
    )
  }

  print() {
    console.log(this.name, 'paper:')
    console.log(this.choiceQuestions.map((q) => q.getAnswer()))
    console.log(this.shortAnswerQuestions.map((q) => q.getAnswer()))
  }
}

const xiaohongPaper = new ExaminationPaper(
  'xiaohong',
  [new Question('A'), new Question('B')],
  [new Question('answer1.'), new Question('answer2.')]
)
xiaohongPaper.print()

// Copy xiaohong's paper
const xiaomingPager = xiaohongPaper.clone() as ExaminationPaper
// Modify name
xiaomingPager.name = 'xiaoming'
// For short answer questions, add a closing word to the end
xiaomingPager.shortAnswerQuestions.forEach((q) =>
  q.setAnswer(q.getAnswer() + `That's all, thanks!`)
)
xiaomingPager.print()

首先,小红实例化 ExaminationPaper 进行作答。然后,小明通过调用小红试卷的 clone 方法复制出一份新的实例,修改名字为 xiaoming,同时为了避免雷同,还"机智"地在每道简答题答案后面加上了一段特有结束语。这样,小明不费吹灰之力就白嫖了一份答案。

从这个例子来看,原型模式很简单,关键在于实现 Prototypeclone 方法,并注意对复杂类型的属性进行深度拷贝。

而对于 Rust 来说,借助其强大的宏的特性,实现原型模式则更加方便。

Rust

rust 复制代码
#[derive(Clone)]
struct Question {
    answer: String,
}

impl Question {
    fn new(answer: &str) -> Self {
        Self {
            answer: answer.to_string(),
        }
    }

    fn get_answer(&self) -> &str {
        self.answer.as_str()
    }

    fn set_answer(&mut self, answer: String) {
        self.answer = answer
    }
}

#[derive(Clone)]
struct ExaminationPaper {
    choice_questions: Vec<Question>,
    short_answer_questions: Vec<Question>,
    name: String,
}

impl ExaminationPaper {
    fn new(
        name: &str,
        choice_questions: Vec<Question>,
        short_answer_questions: Vec<Question>,
    ) -> Self {
        Self {
            name: name.to_string(),
            choice_questions,
            short_answer_questions,
        }
    }

    fn print(&self) {
        println!("{} paper:", self.name);
        println!(
            "{}",
            self.choice_questions
                .iter()
                .map(|q| q.get_answer())
                .collect::<Vec<_>>()
                .join(" ")
        );
        println!(
            "{}",
            self.short_answer_questions
                .iter()
                .map(|q| q.get_answer())
                .collect::<Vec<_>>()
                .join(" ")
        );
    }
}

fn main() {
    let xiaohong_paper = &ExaminationPaper::new(
        "xiaohong",
        vec![Question::new("A"), Question::new("B")],
        vec![Question::new("answer1."), Question::new("answer2.")],
    );
    xiaohong_paper.print();

    let xiaoming_paper = &mut xiaohong_paper.clone();
    xiaoming_paper.name = "xiaoming".to_string();
    for q in xiaoming_paper.short_answer_questions.iter_mut() {
        q.set_answer(format!("{} {}", q.get_answer(), "That's all. Thanks!"));
    }
    xiaoming_paper.print();
}

可以看到,我们并没有给 ExaminationPaperQuestion 实现 clone 方法,而只是在类型前面加上了 #[derive(Clone)]

当在一个结构体或枚举类型上添加 #[derive(Clone)] 派生宏时,Rust 编译器会自动为该类型生成一个 Clone trait 的实现。

对于结构体类型,自动生成的 Clone trait 实现会逐个克隆每个字段,并返回一个新的结构体对象。这意味着每个字段都必须实现 Clone trait 或是基本类型(如整数、浮点数等),否则编译器会报错。

对于枚举类型,自动生成的 Clone trait 实现会逐个克隆每个变体,并返回一个新的枚举对象。同样地,每个变体中的字段都必须实现 Clone trait 或是基本类型。

这样通过使用 #[derive(Clone)],我们可以轻松地为自定义类型生成克隆功能,且无需手动实现 Clone trait,可以减少很多样板代码。

欢迎关注公众号"前端游"

相关推荐
ai超级个体18 分钟前
别再吹牛了,100% Vibe Coding 存在无法自洽的逻辑漏洞!
前端·ai·ai编程·vibe coding
Mike_jia41 分钟前
🎓 OpenMAIC 终极指南:清华开源的多智能体 AI 互动课堂平台
前端
踩着两条虫1 小时前
告别低代码“黑盒”!VTJ.PRO 2.0:用AI与自由重塑Vue3开发
前端·低代码·ai编程
OpenTiny社区1 小时前
WebAgent :基于 MCP 协议打造的智能应用“超级路由器”
前端·agent·mcp
dweizhao1 小时前
别再用 Figma 画线框图了,Google 这款免费工具直接出 UI 稿
前端
han_2 小时前
JavaScript设计模式(五):装饰者模式实现与应用
前端·javascript·设计模式
ProgramHelpOa2 小时前
Amazon SDE Intern OA 2026 最新复盘|70分钟两题 Medium-Hard
java·前端·javascript
smchaopiao2 小时前
如何用CSS和JS搞定全屏图片展示
前端·javascript·css
酉鬼女又兒2 小时前
零基础快速入门前端CSS Transform 与动画核心知识点及蓝桥杯 Web 应用开发考点解析(可用于备赛蓝桥杯Web应用开发)
开发语言·前端·css·职场和发展·蓝桥杯·html
山川行2 小时前
Python快速闯关8:内置函数
java·开发语言·前端·笔记·python·学习·visual studio