简体   繁体   English

如何简化重复功能逻辑

[英]How can I simplify repeating function logic

Question

I'm wondering if there is a way to improve the current structure of some functions in my program since I feel there is a fair amount of unwanted repetition happening.我想知道是否有办法改进程序中某些函数的当前结构,因为我觉得发生了大量不需要的重复。

Background背景

I'm writing a tiny logger so CLI applications can have prettier text in the terminal.我正在编写一个小型记录器,以便 CLI 应用程序可以在终端中显示更漂亮的文本。 I have a couple functions that add some icons to what is going to stdout such as success() , it takes a message and adds a green checkmark icon to it, same with error() , warn() etc. They can all either add a newline character at the end or ignore it depending on if the user called same() before it.我有几个函数可以向标准输出添加一些图标,例如success() ,它需要一条消息并向其添加一个绿色复选标记图标,与error()warn()等相同。它们都可以添加最后一个换行符或忽略它,取决于用户是否在它之前调用了same()

Currently they use the three functions defined below to decide whether or not to add a newline, and whether or not to add a timestamp.目前他们使用下面定义的三个函数来决定是否添加换行符,以及是否添加时间戳。

Code代码

/// Outputs to stdout with an icon
fn output<T: Display>(&mut self, message: T, icon: LogIcon) {
    let timestamp = self.timestamp();

    if self.same_line {
        print!("{} {}{}", icon, timestamp, message);
    } else {
        println!("{} {}{}", icon, timestamp, message);
    }

    self.same_line = false;
}

/// Outputs to stderr with an icon
fn output_error<T: Display>(&mut self, message: T, icon: LogIcon) {
    let timestamp = self.timestamp();

    if self.same_line {
        eprint!("{} {}{}", icon, timestamp, message);
    } else {
        eprintln!("{} {}{}", icon, timestamp, message);
    }

    self.same_line = false;
}

/// Outputs to stdout normally
fn output_normal<T: Display>(&mut self, message: T) {
    let timestamp = self.timestamp();

    if self.same_line {
        print!("{}{}", timestamp, message);
    } else {
        println!("{}{}", timestamp, message);
    }

    self.same_line = false;
}

This is how the success function makes use of output function at the moment:这是success函数目前如何使用输出函数的方式:

pub fn success<T: Display>(&mut self, message: T) {
    self.output(message, LogIcon::CheckMark);   
} 

The same applies for all other functions, they either output to stderr or stdout .这同样适用于所有其他函数,它们输出到stderrstdout

You could change same_line into line_ending .您可以将same_line更改为line_ending Instead of storing true, you would store \\n and always use print!("... {}", ..., &self.line_ending) .而不是存储真,你会存储\\n并始终使用print!("... {}", ..., &self.line_ending) I would also add a function pop_line_ending() which returns the stored line ending and clears it.我还将添加一个函数pop_line_ending() ,它返回存储的行结尾并清除它。

You can make a function that is generic over implementations of std::io::Writer , and also accepts std::fmt::Arguments , which is the formatting arguments applied at compile time to the input string in a way that can be conveniently passed around.您可以创建一个在std::io::Writer实现上通用的函数,并且还接受std::fmt::Arguments ,这是在编译时以一种可以方便的方式应用于输入字符串的格式化参数路过。

use std::{fmt, fmt::Display, io};

fn write_output<W: io::Write>(&mut self, args: fmt::Arguments, newline: bool, mut writer: W) {
    write!(writer, "{}", args).unwrap();
    if newline {
        write!(writer, "\n").unwrap();
    }
    writer.flush().unwrap();
}

Then pass either stdout() or stderr() as required in each call, along with the other arguments:然后在每次调用中根据需要传递stdout()stderr()以及其他参数:

fn output<T: Display>(&mut self, message: T, icon: LogIcon) {
    let timestamp = self.timestamp();
    self.write_output(
        format_args!("{} {}{}", icon, timestamp, message),
        !self.same_line,
        io::stdout(),
    );
    self.same_line = false;
}

fn output_error<T: Display>(&mut self, message: T, icon: LogIcon) {
    let timestamp = self.timestamp();
    self.write_output(
        format_args!("{} {}{}", icon, timestamp, message),
        !self.same_line,
        io::stderr(),
    );
    self.same_line = false;
}

fn output_normal<T: Display>(&mut self, message: T) {
    let timestamp = self.timestamp();
    self.write_output(
        format_args!("{}{}", timestamp, message),
        !self.same_line,
        io::stdout(),
    );
    self.same_line = false;
}

I like Peter Hall's solution, but I think it can be simplified.我喜欢 Peter Hall 的解决方案,但我认为它可以简化。 I got rid of the fmt::Arguments argument and passed in the message and optional icon.我去掉了fmt::Arguments参数并传入了消息和可选图标。 Also got rid of the newline argument and used the member variable directly.也去掉了newline参数,直接使用了成员变量。

use std::{fmt, fmt::Display, io};

struct LogIcon {}

impl Display for LogIcon {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), std::fmt::Error> {
        write!(f, "[ICON]")
    }
}

struct Logger {
    same_line: bool,
}

impl Logger {
    fn write_output<T: Display, W: io::Write>(
        &mut self,
        message: T,
        icon: Option<LogIcon>,
        mut writer: W,
    ) {
        if let Some(icon) = icon {
            write!(writer, "{} ", icon);
        }
        write!(writer, "{}{}", self.timestamp(), message);

        if self.same_line {
            write!(writer, "\n").unwrap();
        }
        writer.flush().unwrap();
        self.same_line = false;
    }

    fn output<T: Display>(&mut self, message: T, icon: LogIcon) {
        self.write_output(message, Some(icon), io::stdout());
    }

    fn output_error<T: Display>(&mut self, message: T, icon: LogIcon) {
        self.write_output(message, Some(icon), io::stderr());
    }

    fn output_normal<T: Display>(&mut self, message: T) {
        self.write_output(message, None, io::stdout())
    }

    fn timestamp(&self) -> &'static str {
        "A TIMESTAMP"
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM