-3

Writing code, in my opinion, usually involves 2 kinds of code: logical and functional code.

While the logical part of the code always differs between every app and its goals, often the functional part doesn't change that much (especially the low level simple functional code) and is often reused a lot in the same project.

For example, in my app I use a lot of File related code. So I created a dedicated util class like this:

public final class FileUtils {

/* Constants */
private static int DEFAULT_BUFFER_SIZE = 1024;
public static final String PACKAGE_FILES_DIR = MyApplication.getContext().getFilesDir().getAbsolutePath(); // Usually: /data/data/package name/files
public static final String MAIN_LOCAL_STORAGE = Environment.getExternalStorageDirectory().getAbsolutePath(); // Usually:


/*
 * ================
 * Constructor
 * ================
 */

private FileUtils() {}


/*
 * ================
 * Helper methods
 * ================
 */

/**
 * Write text to file.
 * <a>{@see javadoc at the beginning of this class}</a>
 *
 * @param path     the path
 * @param fileName the file name
 * @param text     the text
 */
public static void writeTextToFile(String path, String fileName, String text) {

    File file = new File(path, fileName.toString());

    try {
        file.createNewFile();
        FileOutputStream fos = new FileOutputStream(file);
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fos, "UTF-8");
        Writer out = new BufferedWriter(new OutputStreamWriter(fos, "UTF-8"));
        out.write(text.toString());
        out.close();
        fos.close();
        outputStreamWriter.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

/**
 * Check and create sub dierctory.
 * <a>{@see javadoc at the beginning of this class}</a>
 *
 * @param path       the path
 * @param folderName the folder name
 */
public static void checkAndCreateSubDierctory(String path, String folderName) {
    // Check and if needed, create logs subdirectory
    File subDir = new File(path, folderName);
    if (!subDir.exists()) {
        subDir.mkdir();
    }
}

public static void createZip(){
// create a zip including several files
}
}

The thing is, I haven't seen much code written this way. The pro's and con's that I see in this approach are:

Pro's:

  • Makes the logical part of the code much clearer

  • Functional code is not rewritten and is accessible from anywhere

  • To my knowledge, the point of static is code that doesn't need to be instantiated and is common to all.

Con's:

  • It can result in a very large amount of static code.

Another possible approach would be:

public class FileUtils {

/* Constants */
private static int DEFAULT_BUFFER_SIZE = 1024;
public static final String PACKAGE_FILES_DIR = MyApplication.getContext().getFilesDir().getAbsolutePath(); // Usually: /data/data/package name/files
public static final String MAIN_LOCAL_STORAGE = Environment.getExternalStorageDirectory().getAbsolutePath(); // Usually:


/*
 * ================
 * Helper methods
 * ================
 */

/**
 * Write text to file.
 * <a>{@see javadoc at the beginning of this class}</a>
 *
 * @param path     the path
 * @param fileName the file name
 * @param text     the text
 */
public void writeTextToFile(String path, String fileName, String text) {

    File file = new File(path, fileName.toString());

    try {
        file.createNewFile();
        FileOutputStream fos = new FileOutputStream(file);
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fos, "UTF-8");
        Writer out = new BufferedWriter(new OutputStreamWriter(fos, "UTF-8"));
        out.write(text.toString());
        out.close();
        fos.close();
        outputStreamWriter.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

/**
 * Check and create sub dierctory.
 * <a>{@see javadoc at the beginning of this class}</a>
 *
 * @param path       the path
 * @param folderName the folder name
 */
public void checkAndCreateSubDierctory(String path, String folderName) {
    // Check and if needed, create logs subdirectory
    File subDir = new File(path, folderName);
    if (!subDir.exists()) {
        subDir.mkdir();
    }
}

public void createZip(){}

}

Pro's:

  • Doesn't use static code

Con's:

  • Every time I would like to use the code, I would need to instantiate the class, something that could lead to a creation of a lot of objects

Of course, there is always the option to just write the code locally in every class, but that to my knowledge also violate OOP principles and results in a greater amount of code that could be saved...

I searched about this a lot, and couldn't find concrete answers or approach.
Is this an acceptable approach to Object Oriented Programming?
Can someone explain why and what are alternatives to this approach?

14
  • 3
    Questions containing words like "acceptable" are not answerable unless you specify your criteria for judging what you consider acceptable and not acceptable (hopefully without resorting to tautologies like "best practice"). Aug 2, 2017 at 20:25
  • Possible duplicate of What are the valid uses of static classes?
    – gnat
    Aug 2, 2017 at 20:29
  • @gnat How is my question duplicate to what you referred to?! I'm asking about implementing reusable code, whether with static code or not, and the thread you referred to refers to all that is relevant to static... Aug 2, 2017 at 20:33
  • 3
    Your question doesn't provide any code examples other than static ones. Aug 2, 2017 at 20:35
  • 1
    You haven't explained in your question why you think static code is a problem in and of itself. Also, you haven't clarified your use of the word "acceptable." Aug 2, 2017 at 20:55

2 Answers 2

1

These two things here are not "pros" at all:

  • Functional code is not rewritten and is accessible from anywhere

  • To my knowledge, the point of static is code that doesn't need to be instantiated and is common to all.

"Accessible from anywhere" and "common to all" means you are not managing your dependencies at all. Change something in one place and there's no telling how many parts of your code will be affected. This is the opposite of good design. It's as bad as using global variables everywhere.

Symbols, including variables and function names, should be scoped as tightly as possible, and only made available to those parts of the system that need them. What's more, when you form a dependency, it should be on an interface, not a concrete implementation (see Liskov Substitution Principle). Static methods can't participate in an interface.

9
  • You should not use interfaces unless needed. Liskov substitution is not about interfaces, it is about code correctness in the face of a sub-component swap. It involves either an interface or a base class equivalent in a typical OO language. Aug 3, 2017 at 17:33
  • Either way, static methods don't fit.
    – John Wu
    Aug 3, 2017 at 18:06
  • Static methods don't fit, but there is no instance data, so instance methods are not appropriate either. Substitution of one type for another is useful sometimes, but why is it needed in this case? There was no requirement in the original question. Aug 3, 2017 at 19:21
  • Substitution of one type for another is essential for unit testing, where a test stub is substituted for a dependency that is being isolated away. Also essential for the D in SOLID.
    – John Wu
    Aug 3, 2017 at 19:33
  • You can substitute one type in another in a static method by making the parameters interface types. Aug 3, 2017 at 19:52
1

Object-oriented practitioners typically object to your static method approach for a number of reasons:

  1. You don't get OO encapsulation.
  2. You can't use mock objects with a static class.
  3. Static methods can hold static state, which is difficult to test.
  4. Your static method may depend on other static methods that are broken.

Generally these objections arise from people who are accustomed to programming using objects. Before there were objects, there were procedural languages like Fortran, Pascal and C. Objects became commonplace in the programming landscape because they provide a familiar, easy to use container for encapsulating data and related behavior.

But Linus Torvalds is living proof that you can program rather large systems without ever touching an object (he programs in C, which isn't an object-oriented language), using what are essentially structures and methods.

So let's look at the objections in turn:

You don't get OO encapsulation

True. But there are ways around that. In C, you can do it by following simple, sensible rules for naming things and organizing your libraries and header files. Is that a lot of work? It can be. It can certainly be more work than simply using namespaces and classes, but I've also seen the convenience of namespaces and classes abused to create endlessly-bloated architectures.

You can't use mock objects with a static class

But you don't need them, either. A proper static method doesn't require mocks, stubs, interfaces or any of the other machinery typically reserved for OO unit testing. You just hand the method some parameter values and assert the return result. That's it.

Static methods can hold static state, which is difficult to test

Then don't hold static state. You're not going to need it anyway, unless you're writing a logger or creating a poor man's singleton.

Your static method may depend on other static methods that are broken

Presumably, you have test methods that exercise that other, presumably broken method, eh?

The OO developers examined this problem with respect to private methods, and have reached the opposite conclusion: you shouldn't have to test your private methods because they're already being tested indirectly through your public methods!

4
  • Thank you for the points you pointed out... All of that is understood... So what are the alternatives? Should I use functional methods locally in every class? Doesn't that also violate OOP principles? Aug 2, 2017 at 21:00
  • Um, what prevents you from doing exactly what you're doing now? Aug 2, 2017 at 22:07
  • 1
    The issue with statics and mocking isn't that you can't use mocks to test a static method - you can, if it takes an object argument. The issue is that if you want to test some other method, which depends on the static you can't easily replace it with a mock or similar for the purpose of the test. For simple utility methods that may not matter, but typically a unit test would avoid calling a real writeTextToFile() function.
    – bdsl
    Aug 2, 2017 at 23:05
  • This contains some generalizations about static methods... I am sure your link about static methods resolves this, but if there is no instance data needed, of course methods should be static. In the original code examples, I saw no need for instance data. Aug 3, 2017 at 17:31

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