The language is PHP. I have several functions that relate to each other (and sometimes use each other) that I've decided to couple under the same class.

Here are my reasonings, do keep in mind that I'm limited by what I don't yet know in terms of how my code will be used:

These functions have no clear need for a class on their own.

These functions I only need as small bits for random events throughout my other codebase.

These functions I see as a "package of methods that relate to a certain scope", e.g functions that deal with file functionalities (write / read / append, etc.)

These functions help me avoid over-Dependency Injecting; while the classes that use these methods cannot work without these methods, as such, they're depdendent on these methods, it makes no sense to DI the class that contains these methods because it over-complicates code that should be simple.

These functions are a core part that should never be touched, they're provided to you "as is". Static methods cannot be overwritten and this is a plus in my case. Under no circumstance do I want developers to meddle with them.

It is more elegant to write use Package; than require_once for every file, using Composer will leave the functions out in the global scope which I don't want.

And here's my actual code, just stripped out of functionality:

class FilesHelpers
{
    public static function saveFileToDisk(){}

    public static function saveExportFile(){}

    public static function getFilePath(){}
}

As you can see, these functions all deal with files, but as stated previously, they rarely touch each other and are generally able to stand on their feet alone, without needing other functions.

I simply say use FilesHelpers and call whatever method I need.

I want these functions to be readily accessible, but I thought that's not a great idea since I don't really need them except for the places where I need them, while making them static doesn't help me with this, in my mind, a set of static functions that don't care about each other in a class are more "decouples" than non-static functions that require object instantiation.

If I were to put it in other words: I don't want to have the classes that use these functions have dependencies because they can easily be swapped, if you use my class, you use these methods, no discussion, if you want to do the process in some other way, fine, you have the main interface that you can write to and you don't need to use my static functions.

Is this the right approach?

  • So the only reason is so that you can have use FilesHelpers;? – immibis Nov 9 at 3:30
  • @immibis That's exactly the question, are my supposed advantages such as them not being in the global scope (when they don't need to be) real advantages? I'm just trying to find out if I'm not tricking myself. // For your edit: They're not in global scope, I can use them only where I need them. I'd like for a way to supposedly have them available when they're needed without global scope if it makes sense. If I include a file in Composer, it simply leaves them in the global scope. – coolpasta Nov 9 at 3:30
  • 2
    If it helps your decision, Microsoft saw fit to create several classes like this. The File class and the Math class are just two examples. – Robert Harvey Nov 9 at 5:46
  • 3
    In .NET and Java this is done due to the lack of free functions. I think a better reference for PHP might be C++ and I believe this pattern is usually discouraged there, so I'd agree with @kqr's answer. – doubleYou Nov 9 at 6:43
  • @RobertHarvey, but File was a crap decision as we then end up with static methods that have side effects, making testing harder and less robust. IFile, with an implementation of a non-static File class would have been a much better design choice. Using other people's mistakes as justification to make the same mistake yourself is deeply twisted logic. – David Arno Nov 9 at 8:32

This is exactly what namespaces are for, and PHP has got them! The procedures can be freestanding and still not global as long as you give them their own namespace.

You're now using static methods on a class to emulate namespacing. Why not just use namespaces?

  • PHP can autoload classes as needed. It doesn't autoload namespaces, or functions. That's what coolpasta was alluding to with 'It is more elegant to write use Package; than require_once for every file'. – bdsl Nov 9 at 8:16
  • Exactly why @bdsl said. With this in find, is your answer still standing? – coolpasta Nov 9 at 20:58

I'll try and address your question from a PHP point of view. I think what you're looking for is to define them in a trait. Quoted directly from that link, emphasis is mine.

Traits are a mechanism for code reuse in single inheritance languages such as PHP. A Trait is intended to reduce some limitations of single inheritance by enabling a developer to reuse sets of methods freely in several independent classes living in different class hierarchies. The semantics of the combination of Traits and classes is defined in a way which reduces complexity, and avoids the typical problems associated with multiple inheritance and Mixins.

A Trait is similar to a class, but only intended to group functionality in a fine-grained and consistent way. It is not possible to instantiate a Trait on its own. It is an addition to traditional inheritance and enables horizontal composition of behavior; that is, the application of class members without requiring inheritance.

As others have pointed out, static methods are intended to have no side-effects so I'd rather not use them in this case.

Putting them in a trait allows you to pull in the methods where needed, with a simple use statement (see example below). Depending on how your namespaces are and where the trait is located compared to your actual class where you're using the trait, you might have to include an additional use statement outside the class, as you would need to do for any class/interface/trait outside the current namespace.

Class where you're using the trait:

<?php    
namespace Namespace/For/Your/Class;

use Namespace/For/Your/Trait/FileHandlingTrait;
class YourClass {

    use FileHandlingTrait;

}

Actual trait:

<?php    
namespace Namespace/For/Your/Trait;

trait FileHandlingTrait {

    public function saveFileToDisk(){}

    public function saveExportFile(){}

    public function getFilePath(){}

}

I'd suggest the first rule of static functions is that they must be "pure", ie they must be deterministic and side-effect free. saveFileToDisk is a classic example of a function that has side effects as it writes to disk. So when you come to test code that uses it, you have to actually write to a file location. This couples your code to those side-effects and creates very brittle tests:

  1. You can't run two tests in parallel in case they both try to read/write to the same file location,
  2. The directory may not exist, so the file write will fail and the test fails,
  3. You may not have permissions on that file, so the write fails and the test fails,
  4. and so on...

So for your example, decouple things from the side effects with an interface:

interface iFilesHelpers
{
    public function saveFileToDisk();
    public function saveExportFile();
    public function getFilePath();
}

and inject an implementation that accesses the file system at runtime, allowing such behaviour to be mocked out in your tests:

class RuntimeFilesHelpers implements iFileHelpers
{
    public function saveFileToDisk() {…}
    public function saveExportFile() {…}
    public function getFilePath() {…}
}
  • How do you use static methods of an injected implementation? If you need polymorphism doesn't that mean you'd want to use instance methods instead of statics? – bdsl Nov 9 at 14:08
  • @bdsl. oops poor proof reading on my part. Those functions weren't supposed to be static! Edited to correct that. – David Arno Nov 9 at 14:50
  • They're still static in the interface - is that what you want? – bdsl Nov 9 at 15:52
  • @bdsl, OK, now I'm hanging my head in shame :( – David Arno Nov 9 at 15:53
  • Your mock friends will argue that static functions can call other static functions, so they're not really testable anyway. – Robert Harvey Nov 9 at 15:54

No it not a great idea. You have some very specific requirements and if this fits your needs then great.

But generally making a class called SomethingHelper and putting a bunch of static methods in it doesnt make your over all codebase more maintainable.

The Microsoft File statics are a good example. They seem to make things easy, untill you try to do some cross platform PCL stuff where they don't work.

Edit, more info on Portable Class Lib and System.IO as requested

Portable Class Libraries are a way of writing .net code that will run on platforms where the full .net framework is not supported. For example ios.

Because the File.Whatever methods are static and not supported in PCL any code written which uses them becomes not PCL compilant. you have to go through and rewrite all that code if you want to use it. If you had injected an interface instead, you wouldnt have that problem.

This is the maintainablity issue with statics, even though you think you will only ever need one version there's always some edge case down the line that you didn't consider.

  • I haven't used the Microsoft framework. Could you elaborate on the problems you encountered? – user949300 Nov 9 at 6:22
  • 1
    mmmm Ok i edited, but you can prob get a better description by googling – Ewan Nov 9 at 6:34
  • Thanks. While it is unfortunate that Files is not supported in PCL, I'd argue that it is not the fault of grouping/organizing static functions into a class. It's a fault of using static functions that didn't get ported. – user949300 Nov 9 at 7:58
  • @user949300 the point is, when you wrote the code you didnt forsee having to use it somewhere where the static function was unavailable. Its not a fault, its just less flexible to use statics – Ewan Nov 9 at 8:38
  • 1
    @Ewan you didnt forsee having to use it somewhere where the static function was unavailable That is pretty much limited to cross-platform cases. The File static class is considered a framework feature which is why you're arguing that it should be able to write to any file system. However, not every application needs to confirm to framework-level standards. When using static classes in an application that is not consumed as a framework, you can always ensure the static class' availability to anyone who uses your project/DLL file. No matter who ends up referencing that project/DLL. – Flater Nov 9 at 10:44

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.