Specific functions vs many Arguments vs context dependent

后端 未结 6 762
生来不讨喜
生来不讨喜 2020-12-20 17:44

An Example

Suppose we have a text to write and could be converted to \"uppercase or lowercase\", and can be printed \"at left, center or right\".

Specific

相关标签:
6条回答
  • 2020-12-20 17:59

    I suggest more cohesive functions as opposed to superfunctions that can do all kinds of things unless a superfunction is really called for (printf would have been quite awkward if it only printed one type at a time). Signature redundancy should generally not be considered redundant code. Technically speaking it is more code, but you should focus more on eliminating logical redundancies in your code. The result is code that's much easier to maintain with very concise, well-defined behavior. Think of this as the ideal when it seems redundant to write/use multiple functions.

    0 讨论(0)
  • 2020-12-20 18:06

    As you already mentioned, one striking point is readability: writeInUpperCaseAndCentered("Foobar!") is much easier to understand than write("Foobar!", true, true), although you could eliminate that problem by using enumerations. On the other hand, having arguments avoids awkward constructions like:

    if(foo)
      writeInUpperCaseAndCentered("Foobar!");
    else if(bar)
      writeInLowerCaseAndCentered("Foobar!");
    else
     ...
    

    In my humble opinion, this is a very strong argument (no pun intended) for the argument way.

    0 讨论(0)
  • 2020-12-20 18:09

    I'd go for a combination of methods 1 and 2.

    Code a method (A) that has all the arguments you need/can think of right now and a "bare" version (B) with no extra arguments. This version can call the first method with the default values. If your language supports it add default arguments. I'd also recommend that you use meaningful names for your arguments and, where possible, enumerations rather than magic numbers or a series of true/false flags. This will make it far easier to read your code and what values are actually being passed without having to look up the method definition.

    This gives you a limited set of methods to maintain and 90% of your usages will be the basic method.

    If you need to extend the functionality later add a new method with the new arguments and modify (A) to call this. You might want to modify (B) to call this as well, but it's not necessary.

    0 讨论(0)
  • 2020-12-20 18:11

    I prefer the argument way.

    Because there's going to be some code that all the different scenarios need to use. Making a function out of each scenario will produce code duplication, which is bad.

    Instead of using an argument for each different case (toUpper, centered etc..), use a struct. If you need to add more cases then you only need to alter the struct:

    typedef struct {
        int toUpper;
        int centered;
        // etc...
    } cases;
    write( char *str , cases c ){//..}
    
    0 讨论(0)
  • 2020-12-20 18:13

    I'd avoid your first option because as you say the number of function you end up having to implement (though possibly only as macros) can grow out of control. The count doubles when you decide to add italic support, and doubles again for underline.

    I'd probably avoid the second option as well. Againg consider what happens when you find it necessary to add support for italics or underlines. Now you need to add another parameter to the function, find all of the cases where you called the function and updated those calls. In short, anoying, though once again you could probably simplify the process with appropriate use of macros.

    That leaves the third option. You can actually get some of the benefits of the other alternatives with this using bitflags. For example

    #define WRITE_FORMAT_LEFT   1
    #define WRITE_FORMAT_RIGHT  2
    #define WRITE_FORMAT_CENTER 4
    #define WRITE_FORMAT_BOLD   8
    #define WRITE_FORMAT_ITALIC 16
    ....
    write(char *string, unsigned int format)
    {
      if (format & WRITE_FORMAT_LEFT)
      {
         // write left
      }
    
      ...
    }
    

    EDIT: To answer Greg S.

    I think that the biggest improvement is that it means that if I decide, at this point, to add support for underlined text I it takes two steps

    1. Add #define WRITE_FORMAT_UNDERLINE 32 to the header
    2. Add the support for underlines in write().

    At this point it can call write(..., ... | WRITE_FORMAT_UNLDERINE) where ever I like. More to the point I don't need to modify pre-existing calls to write, which I would have to do if I added a parameter to its signature.

    Another potential benefit is that it allows you do something like the following:

    #define WRITE_ALERT_FORMAT  (WRITE_FORMAT_CENTER | \
                                 WRITE_FORMAT_BOLD |   \
                                 WRITE_FORMAT_ITALIC)
    
    0 讨论(0)
  • 2020-12-20 18:20

    I've run into exactly this situation a number of times -- my preference is none of the above, but instead to use a single formatter object. I can supply it with the number of arguments necessary to specify a particular format.

    One major advantage of this is that I can create objects that specify logical formats instead of physical formats. This allows, for example, something like:

    Format title = {upper_case, centered, bold};
    Format body = {lower_case, left, normal};
    
    write(title, "This is the title");
    write(body, "This is some plain text");
    

    Decoupling the logical format from the physical format gives you roughly the same kind of capabilities as a style sheet. If you want to change all your titles from italic to bold-face, change your body style from left justified to fully justified, etc., it becomes relatively easy to do that. With your current code, you're likely to end up searching through all your code and examining "by hand" to figure out whether a particular lower-case, left-justified item is body-text that you want to re-format, or a foot-note that you want to leave alone...

    0 讨论(0)
提交回复
热议问题