如何在没有代码冗余的情况下表示两个真正相似的结构?

时间:2019-07-19 23:45:59

标签: oop rust polymorphism traits

我想为我的项目创建一个小型图形模块。我确实需要有向图和无向图。

如果是C ++或Java,我将创建一个抽象类Graph,以实现深度优先搜索,广度优先搜索以及2个针对特定实现或特定方法的子类Directed和Undirected。

我确实读过本书的OOP部分;但是,我该如何用特质来表现这种行为?

理想情况下,我可以像这样使用我的mod:

use graph::{UndirectedGraph, DirectedGraph, Graph};


pub fn main() {
    let g1 = Undirectedgraph::new(); // implementing Graph trait
    let g2 = DirectedGraph::new(); // implementing Graph trait
    g1.dfs(); // from Graph
    g2.dfs(); // from Graph
    g1.bfs(); // from Graph
    g2.bfs(); // from Graph

    let _ = g1.has_loop(); // from UndirectedGraph implementation only
    let _ = g2.has_loop() // from DirectedGraph implementation only
}

所以我最终得到了这样的东西;如您所见

在属性和获取方法上仍然有很多冗余:

#[derive(Debug)]
pub struct Node {
    value: i32,
}

pub trait Graph {
    fn get_vertices(&self) -> &Vec<Node>;

    fn print_nodes(&self) {
        self.get_vertices()
            .iter()
            .for_each(|x| println!("{:#?}", x));
    }

    fn bfs(&self) {
        println!("Common implementation");
    }

    fn dfs(&self) {
        println!("Common implementation");
    }

    fn has_loop(&self) -> bool; // should be implemented
}

pub struct DirectedGraph {
    vertices: Vec<Node>,
}

impl Graph for DirectedGraph {
    fn get_vertices(&self) -> &Vec<Node> {
        &(self.vertices)
    }

    fn has_loop(&self) -> bool {
        //some weird stuff
        // specific to DirectedGraph
        true
    }
}

pub struct UndirectedGraph {
    vertices: Vec<Node>,
}

impl Graph for UndirectedGraph {
    fn get_vertices(&self) -> &Vec<Node> {
        &(self.vertices)
    }

    fn has_loop(&self) -> bool {
        //some weird stuff
        // specific to UndirectedGraph
        true
    }
}

2 个答案:

答案 0 :(得分:1)

特质不能隐式访问默认方法实现中的实现类型的数据。也就是说,您无法提供访问Self类型的特定字段的单一特征方法实现,因为无法假定Self具有任何特定字段。在将来对该语言的扩展中,可能允许使用更好的特征来访问字段。请参阅RFC 1546,了解其中的一项建议和相关讨论。但这仍然仅适用于直接现场访问。如果您需要在返回字段之前以任何方式进行操作,即使那样也无济于事。

现在,您需要手动为这两种类型编写Graph的实现,或者,如果实现确实非常相似,则可以使用具有实现内容的宏并调用每种类型一次。两次手动实施是否需要重复次数

答案 1 :(得分:1)

您不能直接从特征中访问数据的属性(请参见 Jimmy Cuadra 答案)。但是,我们可以在 kyle 评论中使用共享的getter和setters。

类似于下面的代码应该可以工作。

trait Graph {
    fn adjacent_edges(&self, v: &Vertex) -> SomeOutput;

    fn dfs(&self, v: &Vertex) -> SomeOutput {
        let adjacent_edges = self.adjacent_edges(v);
        // ...
    }

    fn bfs(&self, v: &Vertex) -> SomeOutput {
        let adjacent_edges = self.adjacent_edges(v);
        // ...
    }
}


struct UndirectedGraph { ... }

impl Graph for UndirectedGraph {
    fn adjacent_edges(&self, v: &Vertex) -> SomeOutput {
        // ...
    }
}


struct DirectedGraph { ... }

impl Graph for DirectedGraph {
    fn adjacent_edges(&self, v: &Vertex) -> SomeOutput {
        // ...
    }
}