面向对象(OOP)

9 篇文章 1 订阅
6 篇文章 0 订阅


提示

所有帖子都会不定期添加补充实战过程中发现的新内容,力争一篇文章涵盖所有内容,避免你多次查找文章,所以最好点赞或收藏,时不时回来看看。


废话

面向对象使代码得到复用,但要求有良好的OOP思维,面向对象有三个特点,封装、继承、多态,在rust里结构体可以当类使用,实现了封装,多态可以纯手写或者定义好Trait组合使用(rust提倡组合优于继承),继承在rust里没有明确的实现方式,经过一翻探索,发现使用Deref解引用来实现相对最好,当然也可以使用AsRef实现显示的类型转换(算是类型强制转换的写法吧)如果你发现更好的,一定记得告诉我。

一段漂亮的代码

use std::ops::Deref;
use std::ops::DerefMut;

// 封装父类或叫基类
struct Control {
    x: u32,
    y: u32,
}

// 基类的实现
impl Control {
    fn paint(&self) {
        println!("control x:{}, y:{}", self.x, self.y);
    }
}

// 封装子类
struct Button {
    control: Control,
    text: String,
}

// 子类的实现
impl Button {
    // 这里的paint是模拟多态,严格来说并不是多态,如果定义包含paint的Trait供父类和子类实现才可以说是严格意义上的多态
    fn paint(&self) {
        println!("button x:{}, y:{}, text:{}", self.control.x, self.control.y, self.text);
    }
}

// 不可变解引用实现
impl Deref for Button {
    type Target = Control;  // 不可变解引用的目标是Control

    fn deref<'a>(&'a self) -> &'a Control {
        &self.control
    }
}

// 可变解引用实现
impl DerefMut for Button {
    //type Target = Control;  可变解引用不需要写目标
    fn deref_mut<'a>(&'a mut self) -> &'a mut Control {
        &mut self.control
    }
}

/// 显示类型转换
impl AsRef<Control> for Button {
    fn as_ref(&self) -> &Control {
        &self.control
    }
}

fn main() {
    // 定义子类对象
    let mut button = Button {
        control: Control {
            x: 10,
            y: 12,
        },
        text: "确定".to_string(),
    };

    {  // 当父类对象使用,配对的{}限制了可变借用的范围,不是必需要写,是编程的严谨性
        let mut control: &mut Control = &mut button;
        control.x = 20;
        control.y = 22;
        control.paint();  // 调用父类的方法,有点super或base的味道了
    }

    button.paint();  // 调用子类自己的方法
    button.as_ref().paint();  // 显式转换为control类型
}

执行结果:

control x:20, y:22
button x:20, y:22, text:确定
control x:20, y:22

在面向对象开发里的类,往往包含字段和方法,如果说你的类里方法很少甚至没有,那么也可以用下面的方式来处理,我是
参考了这篇文章

// 水果特性
trait FruitTrait {
    fn get_type(&self);
}

// 苹果
struct Apple { }
impl FruitTrait for Apple {
    fn get_type(&self) {
        println!("I'm Apple");
    }
}

// 桔子
struct Orange { }
impl FruitTrait for Orange {
    fn get_type(&self) {
        println!("I'm Orange");
    }
}

struct FruitFactory1 {
    subclass: Box<FruitTrait>,
}

impl FruitTrait for FruitFactory1 {
    fn get_type(&self) {
        self.subclass.get_type();
    }
}

struct FruitFactory2<T: FruitTrait> {
    subclass: T
}

impl<T: FruitTrait> FruitTrait for FruitFactory2<T> {
    fn get_type(&self) {
        self.subclass.get_type();
    }
}

pub type OrangeFactory = FruitFactory2<Orange>;

fn main() {
    let apple = FruitFactory1 {
        subclass: Box::new(Apple{}),
    };
    apple.subclass.get_type();

    let orange = OrangeFactory {
        subclass: Orange{},
    };
    orange.subclass.get_type();
}

FruitFactory1和FruitFactory2这两种方案又各有优缺点。
第一种对外来说是一个单独的类型Fruit,这既是优点也是缺点:它的大小是固定的,可以方便的放在Vec里面。外界想要知道具体类型信息的话,可以通过提供一个get_type() 之类的办法来提供。
第二种方案对外来说是一组类型你可以使用不同的impl 规则来为它们添加方法。然后你可以用类似于
pub type OrangeFactory = FruitFactory2;
把类型暴露给其他组件使用,是不是类似C++/Java 中继承设计的方案。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值