Well, it does not matter how big the source code but about readability and good performance. Although we have many dimensions in software development but I see two important things:
- is that the website get fast?
- is that easy for maintenance?
The first question is what customers want. The Second question is developers want. So we do not write beautiful code but write maintainable code. Building software is not easy. Many developers think that is a simple application at beginning so they put everything in once place and feel like a hero because coding so fast. However, breaking code into many classes is also challenge. We would think it carefully. If not we will get out of control with bad naming, getting confused about them all. When doing so we see a lot of files created, feeling like source code growing big. But it does not matter. The matter is that the readable code, well-design. So our code speaks itself, we don't have to ask PO or looking for documents so hard.
class Creature def fly end def walk end def swim end end
How about Creatures that can not fly such as Ant, Dog and Cat? How about creatures that can not swim such as Chicken and Angry Bird? The objects created hold more information that never use, characters are not belongs to objects.
class PrintFormat def print_html end def print_json end def print_text end end
We actually never use three formata at same time. And object will hold even more information if we need other formats in future by adding extra methods to this class. Furthermore if we modify print_json, it impacts all other places using this interface. What happen if we want to print json this way and that way bla bla? Modify print_json method? How about impact of existing code where using this interface? Adding extra method like print_json_another_way ? (lol). If doing this, we fall to a problem that we are not focusing on interface but implementation. The number of public methods will be increased, making messy code. Because class is blueprint for object creation. So it depends. Create many objects will lead us to problem of performance due to object allocations. But objects hold tons of information will consume tons of memory and too many responsibilies. It leads to coupling code. It's hard for reading code and maintenance.
class Printer def print(line) end end class HTMLPrinter < SimpleDelegator def print(line) line = super(line) # extending behavior here end end
class HTMLPrinter attr_reader :printer def initialize(printer = Printer.new) @printer = printer end def print(line) line = printer.print(line) # extending behavior here end end
Why it's good?
- Only one responsibility.
- HTMLPrinter only print html format.
- Printer's objects won't be impacted.
- Readable at first glance.
- Ruled by Open/Close principle: extending behaviour without modification - in order to do this we must be good at separating responsibility. Keep Printer as simple as posible, doing enough thing at developing time but open for expanding. We don't modify Printer class so we don't change attributes and behaviors. Printer's object is like it is. Doing more without impacts.
- Printer and HTMLPrinter are actually using Duck Typing pattern. They are the same cauze they all print like a printer - same interface, behaviour. Class name are different but system treats them like same object. If we inject HTMLPrinter object to existing code to replace Printer object so why not? print method will be working fine. This is Liskov Principle although it's not inheritance. Once again "program to interface not an implementation" point in ruby. Thus we keep interface clean.
- Need to make clear about Open/Close principle. It's not about adding more methods to existing class, not make inheritance to add extra method. Talking about this like we focus on implementation, not interface. No, it's not like that. Doing so will damage Single Responsibility Principle. Think it like this "Doing more without impacts". We don't focus on class level but object level. Because if code is not run, it's trash. Implementing a class like we are planing to create babies (instances).
I'm happy to prioritize readability first. Many classes created is not problem. For instance, we have fifty decorators but we do know the responsibility of each one. At first glance we look at a class we know its responsibility. Thi is readability. Many objects but if they are light objects, it's good as well. We can clean or even not create unnecessary objects. Anyway, separating responsibility in class level is good practice.
Many classes created but at runtime just some of them are run in moments. The numbers of objects are sufficient. These objects hold only necessary information although we see tons of class in source code. Class separated so the object created will holding less information, taking less memory. This is efficient.
Separating concerns, decoupling responsibility somehow avoiding memory leak, making it easy to read as well as maintenance is what we should do.