3

I have a very large class of 59 methods and about 3000 lines of code. I know that's far larger than most people would want, but the class represents a virtual machine, and most of the methods are run time functions, so I think this is one of the times when a large class is warranted.

The question I have is how can I best organise the source code of this class, as I would like to split it up into multiple files.

I originally used John Resig's class pattern. I would export an object literal from each file, combine them together, and then create the class. This works very adequately, but I want to move to ES classes.

I've experimented with the Mixin pattern from MDN. Each file could export a function, and then I can combine them together with reduce.

export default Base => class extends Base { ... }
const inherit = ( ...fns ) => fns.reduce( ( v, f ) => f( v ) )

const Glk = inherit( GlkAPI, DateTime, Fref, Misc, Stream, Style, Window )

This is functional, but it's convoluted, and it adds a lot of layers to the inheritance tree, which is not good for performance.

So I'm wondering if there are any better options.

Things I've considered:

  1. Doing a build time concatenation. The resulting file could be very nice, as if it were a single file to start with, but I'd lose the ability to lint the source code, as the inside of a class is not valid outside of that context. Or I could add a dummy class wrapper around each file, but then I'd need to trim it out when combining, and that's not a simple concatenation.

  2. Use Typescript declaration merging. This sounds good in principle, but I don't think you can merge classes together, and merging a class with a namespace wouldn't work either as the namespace properties would become static class properties.

  3. Create one simple class and then manually augment the prototype with functions exported from the other files. I guess this is my leading option, but I'm wondering what other projects with big classes have done.

  • 1
    Why do you want to split the class into multiple files? 3,000 lines is huge, but not unheard of for exceptional situations. If your IDE can handle it then I would just keep it intact. – Dan Wilson Oct 17 '18 at 17:28
  • 1
    I agree with @DanWilson. If it's not causing you any specific issues, just leave it. In my career, I've seen source files that were 10,000+ lines long. – 17 of 26 Oct 17 '18 at 17:51
  • 1
    Not sure why this got a downvote. IMO its one of the best examples of the kind of questions this site is for. – GrandmasterB Oct 17 '18 at 20:49
  • @Dan Just my personal preference, I like splitting by area of concern. – curiousdannii Oct 17 '18 at 22:06
5

In situations like this, when I have one part of my program that needs to stand alone as a single, self-contained module but contains many thousands of lines of code, I prefer to take a compositional approach to keep things clean and manageable.

Instead of trying to keep all of your functionality in one class, why not break it out into several "internal" classes that your main VirtualMachine class instantiates in its constructor and delegates to? Each class would be in its own file, and you can give your VirtualMachine class a facade interface to abstract the fact that it delegates to multiple objects internally to do what it needs to do (i.e. you can keep its interface the same as it is now even though its functionality is broken up into multiple classes internally).

I know you said that it makes sense for all of the code in question to be in one class, but even so large classes often have discreet subsystems that are responsible for specific things. Maybe the examples you provided (Stream, Style, Window, etc) handle specific concerns in your larger VirtualMachine class? In my opinion it's fine to break these out into separate classes that are only meant to be instantiated and used together in your main class.

If your classes all need access to some shared state, break that out into a separate class as well, instantiate it in the VirtualMachine class constructor, and pass it to all the other classes via their constructors when you create them.

The main benefit of taking this approach over the others you mentioned is that it can be done with a simple and well-understood language construct (a class) and common design patterns (delegation and composition), instead of relying on build magic or esoteric language features to work.

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.