40

A very simple question really and I expect an answer of 'circumstance dictates'. I was wondering however what people's thoughts are on passing parameters to a constructor or a method.

I'll try and set a context for my question:

public interface ICopier
{
   void Copy();
}

public class FileCopier : ICopier
{
   String m_source;
   String m_destiniation;

   FileCopier(String source_, String destination_)
   {
        m_source = source_;
        m_destiniation = destiniation_;
   }

   public void Copy()
   {
        File.Copy(m_source, m_destiniation, true);
   }
}

Or should FileCopier.Copy() accept source_ and destination_ as method parameters?

I want to keep these classes as abstract as possible.

I'm asking this question as I now have other interfaces/classes for Deleting, Renaming and so on, and I want to create a standard for doing this.

Thanks!

adamwtiko
  • 2,865
  • 8
  • 39
  • 47
  • +1 reopen. This has nothing to do with opinion. In fact, you encounter this design consideration on a daily basis. The question is highly relevant. – l33t May 22 '22 at 09:47

11 Answers11

39

It depends :)

Basically, object-orientation states that objects should encapsulate data and behavior. When you pass data as constructor parameters, you indicate that this is the data to encapsulate.

On the other hand, when you pass data as parameters, you indicate that this is data that somehow is less coupled to the object. Once you begin to move towards Data Context Interaction (DCI), lots of objects tend more and more to encapsulate behavior rather than data.

At the same time, the book Clean Code also guides us to limit the number of method parameters, towards the ultimate conclusion that a method with no parameters is the best design of all.

So I would tend towards encapsulating the data by passing it as constructor parameters in order to have simple APIs. It would then look much like a Command object.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • 3
    I completely agree with Mark, but it's important to understand the context. We can't design every application toward the ultimate conclusion that the ideal class will look like "interface IFoo() { void Foo() }". I think it's useful to think as if you're using DI. What are your dependencies (injectables / constructor args) vs what are your runtime values (newables / method args). In the context of a class that needs to copy a single source file to a dest file, hopefully clearly "interface ICopier { void Copy(src, dest); }" makes more sense than "interface ICopier { void Copy(); }"... right? :) – G. Lombard Aug 26 '17 at 04:10
  • I agree with mark to a point. Code is like sculpture. You chisel away at it to bring out its inner beauty and elegance. Unfortunately, we often run into scenarios where other factors come into play, namely performance and efficiency. In the FileCopier example, storing the source and destination is inefficient, especially of FileCopier is a sizeable object. Newing and deleting FileCopier instances to conserve memory will greatly impact performance as well. – ATL_DEV Jan 21 '18 at 13:01
  • 1
    Regrading Clean Code's guidance of limiting the number of method parameters, wouldn't you think that it also applies to constructor arguments as well? – yair Mar 11 '18 at 07:42
  • @yair Yes, I think that it does. – Mark Seemann Mar 11 '18 at 07:50
  • You answered real quick :). So, if it applies to constructors as well, then apparently it doesn't help determine between constructor arguments vs. method arguments, right? – yair Mar 11 '18 at 08:49
  • @yair No, counting the number of arguments alone doesn't provide much insight in that regard, I think. – Mark Seemann Mar 11 '18 at 08:52
  • Doesn't it depend on the usage of the client? So some clients may need to pass different source and destination and others should not be aware of source and destination. To be more flexible I would use method injection. It is easy to create a second interface where the implementation uses constructor injection and just delegates the injected source and destination to the other interface's copy method. I just often face issues concerning the naming of these two interfaces. Maybe ICopier is not the ideal example but recently I had the discussion on an IWifiLoginService – Creepin Nov 11 '18 at 07:47
  • @Creepin If you follow the Dependency Inversion Principle, it does indeed depend on the client. Perhaps this article could be useful: http://blog.ploeh.dk/2017/01/27/dependency-injection-is-passing-an-argument – Mark Seemann Nov 11 '18 at 09:14
  • @MarkSeemann I read your book and am excited for the second edition. It was one of the most valuable books. Thanks for the link – Creepin Nov 11 '18 at 10:44
  • What do you mean Command object? – NeoZoom.lua Dec 17 '19 at 09:09
  • 1
    @Raining https://en.wikipedia.org/wiki/Command_pattern – Mark Seemann Dec 17 '19 at 13:33
19

Pass them to the method. That way you can copy to more than one location without having to reinstantiate FileCopier, and do other operations that may require other params. In this case you can make the methods static, so no instantiation is needed at all. You'll noticed this is the way the C# File class works, for example.

D'Arcy Rittich
  • 167,292
  • 40
  • 290
  • 283
  • 3
    I've gone with this approach. I've decided to pass source_ and destination_ as method parameters as these two bits of data are only meaningful for the copy and not after. I'm going to use constructor parameters for state data such as an 'overwrite' flag as this will apply to many executions of Copy(). – adamwtiko Dec 06 '10 at 14:52
15

It also depends if the application is staless or stateful. On a stateful architecture probably I'd choose passing the parameters on the constructor, allowing me to save that object and call the already initialized instance multiple times. On a stateless application you'd have to create the object's state everytime you create an instance of the object, which is kinda redundant, so I'd choosing passing those parameters on the method for a clearer interface.

t3mujin
  • 1,272
  • 1
  • 9
  • 22
9

Generally speaking, it is better to avoid adding unnecessary state to a class. Meaning, that if some data items are only needed in the context of a specific operation, you would usually prefer to have them only as local variables in the method, and pass them as parameters.

This way your code will also tend to be less error-prone, because there will be less unnecessary state that is shared between methods, so less potential for bugs.

You should pass parameters to the constructor when you want to enforce that the values should never change during the usage of the class.

It probably doesn't make sense to create a FileCopier object that can only copy a specific file, so in your case a method is more appropriate.

On the other hand, if you have to manage state for each copying operation, such as progress tracking, error tracking, completion callbacks etc, it would make more sense to pass the parameters in the constructor, but also add enforcement that will prevent Copy from being called more than once.

Ran
  • 5,989
  • 1
  • 24
  • 26
7

Old question but it seems a lot of people are answering without considering the intent of the ICopier abstraction. As Saurabh stated, making it copy(String, String) (and adjusting the interface accordingly) will mean any other implementations of ICopier will be limited to using this method.

What if you need to extend it to create an EmailCopier that sends a file from a source to a list of emails instead of a destination file? You could write a hacky method that continues to accept (String, String):

ICopier copier = new EmailCopier();
copier.copy(sourceFile, "an@email.com,another@email.com");

Or you could put all this info in the constructor using proper data structures and a simple copy() command:

ICopier copier = new EmailCopier("sourceFile', aListOfEmails);
copier.copy();

To summarise, if the implementations of the interface will have largely different parameters, you should use constructor parameters with a simple command. If all the implementations are operating on the same inputs, method parameters are fine.

Community
  • 1
  • 1
mmmdreg
  • 6,170
  • 2
  • 24
  • 19
7

I recently found the following rule "If the parameter is used because of the implementation then put it in the constructor, if the parameter is used because of the interface then put it in the method".

The problem is you don't have any comment above the Copy method :

  • If it's "copy one file from a destination to a source" then you should add source and destination parameters because you speak about it on the interface description
  • if it's "when overriden copy something" then add it on the constructor because only your FileCopier implementation will need it
remi bourgarel
  • 9,231
  • 4
  • 40
  • 73
2

It is all about usage;

Which one is easier for you?

FileCopier f = new FileCopier(sourceFrom,sourceTo);
f.Copy();

or

FileCopier f = new FileCopier();
f.Copy(sourceFrom,sourceTo);

I prefer the second not only because it is easier to read but also, you don't have to re-create the object once your paths are changed. It is not a very good idea to set sources in the ctor. But again, it may only be me.

Pabuc
  • 5,528
  • 7
  • 37
  • 52
2

i would prefer constructor taking arguments becuase responsibility of FileCopier is to copy the file from source to destination and the Interface which it is implementing can be used with other hetrogeneous classes so if you provide the parameters to the Copy() function , it will become more specific but in the current state , it can be used in many places which requires the copy functionality

TalentTuner
  • 17,262
  • 5
  • 38
  • 63
1

Unless there are a lot of other methods that operate on the source and destination files that you've not shown us, FileCopier should probably be a static class and not store the file paths as class-level variables at all. Rather, all of the individual methods of the class should accept the files to be operated upon (and any other necessary information or settings) as parameters. That way, you won't have to instantiate an instance of the class each time you want to copy a file.

FileCopier fc = new FileCopier(src, dest);
fc.Copy();

versus

FileCopier.Copy(src, dest);

The best example of what I'm talking about here is the System.Math class in the .NET Framework. The System.IO.File class also works this way (and begs the question of why you're recreating it).

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • Even if there are non-static methods the file copy func should be static, perhaps with a parameterless override which copied the 'member' files. – Patrick Dec 06 '10 at 14:34
  • In the first example, nothing prevents you from writing `new FileCopier(src, dest).Copy();` – Mark Seemann Dec 06 '10 at 14:35
  • 1
    @Patrick: While in general I agree with you, it still depends on the design of the class. What hints to me that it may not be just a standard utility class for copying files (and therefore may merit a slightly unusual implementation) is that it's duplicating a class in the Framework already provided for doing exactly that. If the intended usage case involves performing an extended series of operations on two specific files, and there's a reason for those files to be closely coupled with instances of the class, there may be no need to make it static. – Cody Gray - on strike Dec 06 '10 at 14:37
  • @Mark: Of course not. That wasn't really the point of the example. You still have to instantiate an object of that type in order to call its `Copy` method. Is there a reason you think that is preferred? – Cody Gray - on strike Dec 06 '10 at 14:40
  • 1
    @Cody Gray: Yes. Static classes can't implement interfaces, so they lead towards tightly coupled code. – Mark Seemann Dec 06 '10 at 14:41
  • 1
    The advantage of having FileCopier be a non-static class is that a routine which is going to need to copy files would be able to accept an ICopier object. It may be that today the file copier is supposed to simply copy files from one place to another, but an ICopier object could add additional functionality such as name remapping, file compression, improved data cachine, etc. If a routine calls FileCopier.Copy(src, dest) that's the only method it will ever be able to use. If instead it accepts an ICopier called theCopier, and calls theCopier.Copy(src, dest) it could use other methods. – supercat Dec 06 '10 at 17:04
1

wrt to your context it is better to pass parameter to method if you only have a copy method within your class . But passing them to constructor can be helpful if you have to do multiple operations using the same object.

for example :

    public class FileCopier : ICopier
    {
       String m_source;
       String m_destiniation;

       FileCopier(String source_, String destination_)
       {
            m_source = source_;
            m_destiniation = destiniation_;
       }

       public void Copy()
       {
            File.Copy(m_source, m_destiniation, true);
       }

       public void DeleteSource()
       {
       }

       public void DeleteCestination()
       {
       }

  etc...
    }
wizzardz
  • 5,664
  • 5
  • 44
  • 66
1

Strange that no one mentioned about your interface ICopier.

Just looking at this interface, I would pass the arguments in the method itself. Since, it is a contract it should also force the objects implementing this interface to accept the legitimate arguments to complete/implement the task.

If ICopier is asking for implementation of Copy(), it should also provide the values/arguments needed to accomplish the task.

Manish Basantani
  • 16,931
  • 22
  • 71
  • 103