Home - ThorbenKuck/Keller GitHub Wiki
The Keller-Framework is a basic foundation for any Application/Framework/... which provides ground-work for some of the most common (and even some extravagant) patterns and design principles.
Keller is designed modular, so you don't need to bloat your application.
Keller currently consists of the following modules:
-
keller-core
This module holds multiple bases for working on and with Keller. It expands on the Collections provided by Java, provides annotations, Pipelines, .. -
keller-cache
This module provides a simple, observable, low-level cache. -
keller-comm
A simple implementation of the Command-Pattern. Commands are queued within an Enforcer for execution. -
keller-di
Not a DI module! keller-di provides an annotation-driven (which are querried at runtime, so no code-generation) di-management -
keller-event
google.guava.EventBus 2.0. This EventBus is abstracted through the strategy pattern, which means the dispatching process can be done however you like -
keller-mvp
Harder to understand, keller-mvp provides a base for using the Model-View-Presenter Pattern with whatever UI-API. -
keller-nio
NIO in itself is not the best designed part of the JDK. NIO provides a simple reactive wrapper for NIO. -
keller-repo
Sometimes you need a repository that is connected to whatever. This will be the foundation you are looking for. -
keller-state
Ever wanted to turn your existing business-logic into a state-pattern without the need of implementing another interface? keller-state provides an annotation-driven way for this, on the foundation of keller-di.
To use Keller, you have to understand it's basic design-principle: interface driven design.
You will rarely (if ever) see the keyword new when using Keller. Keller is meant to be used interface-driven. This means, you request an instance of the provided interface, by the interface. You will never see the actual implementations, which are normally named as following: NativeInterfaceName
.
and this is a good thing! Whenever i see a needed cast in an API, i am asking myself: Who the hell designed this s***? (looking at you NIO). You can ALWAYS design you application in a way, that does not require any cast at all. In fact, if your design implies casts, you should think about what you just did! Go into the corner and shame on you! Most times, if you design with casts in mind, you have a verry deep dependency tree, which means, that changes at any point more deep in will require a butload of work.
This is why Keller is interface driven. You won't be able to cast to a specific implementation of an interface. You can surely look at the code an see "Well, calling Value.of(...)
returns a NativeValue
instance", but you can't ask for the NativeValue
because it is package private.
This Design-Principle requires you (as a developer) to think about what you want to expose and provide before you implement the functionallity. It might sound like more work, but it is not. Designing this way also brings with it, the freedome of changing the implementation as you please. Since noone can access the specific implementation, noone will be hurt if you change a specific, non public method-name or something (as long as the application still runs ofcourse). The Module you design is closed by nature.
Since Java 8, interfaces may have static methods. This is what is used widely in this framewok. If you ask yourself, what you should use to instantiate any interface, take a look at the interface. Some of the time, the method that provides you with an instance of the interface if called create
. Some times it is called unifiedCreation
. Other times, it is called the base it is founded on (like queue
, or stack
).
So, for example, creating a Pipeline is done the following way:
Pipeline<Foo> pipeline = Pipeline.unifiedCreation();