Skip to content
This repository has been archived by the owner on Dec 25, 2019. It is now read-only.

Commit

Permalink
Merge pull request #7 from beworker/feature-lifecycle-inheritance
Browse files Browse the repository at this point in the history
Feature lifecycle inheritance
  • Loading branch information
Sergej Shafarenka committed Sep 27, 2016
2 parents c285f48 + 6f065b9 commit 5a5ae71
Show file tree
Hide file tree
Showing 32 changed files with 1,052 additions and 296 deletions.
83 changes: 10 additions & 73 deletions README.md
@@ -1,68 +1,14 @@
# Featured
helps you to split activity or fragment code into truly decoupled, testable and maintainable features.
This library will help you to split activity or fragment code into truly decoupled, testable and maintainable features.

Features are hosted by a `FeatureHost` which resides in an activity or a fragment. Features communicate to each other through the feature host by means of events as shown in the picture below.
Features are small units sharing same lifecycle. They are hosted by a `FeatureHost` class which, in turn, resides in an activity or a fragment. Features communicate to each other through the feature host by means of events as shown in the diagram below.

![diagram][1]

# How to start with featured desing?
# Documentation

1) First you need to think of splitting your activity into independent components communicating via events. Start with standard lifecycle events like `onCreate()`, `onStart()` etc. and add your events to them.

2) Create basis feature class and add all events of all possible components, including standard lifecycle events.
```java
public class SampleFeature extends Feature<SampleFeatureHost> {
@FeatureEvent protected void onCreate(@NonNull CoordinatorLayout parent) {}
@FeatureEvent protected void onStart() {}
@FeatureEvent protected void onFabClicked() {}
@FeatureEvent protected void onStop() {}
@FeatureEvent protected void onDestroy() {}
}
```
Because your feature is called `SampleFeature`, generated feature host class will be called `SampleFeatureHost`. You need to use this name as parameter of extended `Feature` class. Save and build `SampleFeature` class. Feature host will be generated and sources will compile successfully.

3) Now implement your features by extending basis `SampleFeature` class.
```java
public class ToolbarFeature extends SampleFeature {
@Override protected void onFabClicked() {
// handle on-click-event here
}
}

public class FabFeature extends SampleFeature implements View.OnClickListener {
@Override public void onClick(View view) {
// dispatch on-click-event to other features
getFeatureHost().dispatchOnFabClicked();
}
}
```
Generated feature host class contains dispatch-methods for each event method of `SampleFeature`. Use these methods to dispatch events to all features. You can call those methods from activity or from inside any feature.

4) Register features to the feature host class in your activity or a fragment.
```java
public MyFragment extends Fragment {
private SampleFeatureHost mFeatureHost;

@Override public void onCreate(Bundle savedInstanceState) {
...
CoordinatorLayout parent = (CoordinatorLayout) findViewById(R.id.coordinator);

// create feature host and add a feature we created
mFeatureHost = new SampleFeatureHost(getContext())
.with(new FabFeature())
.with(new ToolbarFeature());

// dispatch event to all registered features
mFeatureHost.dispatchOnCreate(parent);
}

// dispatch other events correspondingly
@Override public void onStart() { mFeatureHost.dispatchOnStart(); }
@Override public void onStop() { mFeatureHost.dispatchOnStop(); }
@Override public void onDestroy() { mFeatureHost.dispatchOnDestroy(); }
}
```
Your fragment become very simple and whole application code gets split into separated features with very clean responcibility. You can add new features or disable existing features depending on the device configuration etc. See `featured-sample` project for more detail.
- [Quick Start][2]
- [Featured Design][3]

# Use with Gradle

Expand All @@ -89,22 +35,11 @@ android {
}
dependencies {
apt "de.halfbit:featured-compiler:0.0.2"
compile "de.halfbit:featured:0.0.2"
apt 'de.halfbit:featured-compiler:0.1.0'
compile 'de.halfbit:featured:0.1.0'
}
```

# Featured design
Here is some rules and implementaiton details helping you to become familiar with the library and write cleaner code.
- Features must not access other features directly but shall always interoperate through feature callbacks. By following this rule you will be rewarded with simple, maintainable and testable code later on.
- You can define as many event callbacks as you need and they can have as many parameters as you need.
- Generated feature host class will contain a `dispatchOn<Event>()` method for each feature's `on<Event>()` method.
- To dispatch an event to all features you just need to call its corresponding `dispatchOn<Event>()`.
- It is allowed to call a `dispatchOn<AnotherEvent>()` method from a feature's `on<Event>()` callback. Feature host will make sure that currently running dispatch loop finishes and current event gets dispatched to all features before the new event gets dispatched.
- This make event dispatching to be asynchronous. It means you cannot assume that a `dispatchOn<Event>()` finishes, corresponding event has been delivered to all features. Actual event dispatching can happen also later in time. If you want to be notified after an event has been dispatched, you need to use `@FeatureEvent(dispatchCompleted = true)` and provide corresponding `OnDispatchCompleted` callback in `dispatchOn<Event>()` method. Provided callback will be notified after event dispatching finishes.
- Current implementation is intended to be used in MainThread. This is the only thread strategy implemented at this time.
- Featured is being actively developed and new library features are to be expected.

# License
```
Copyright 2016 Sergej Shafarenka, www.halfbit.de
Expand All @@ -122,4 +57,6 @@ See the License for the specific language governing permissions and
limitations under the License.
```

[1]: web/diagram.png
[1]: docs/images/diagram.png
[2]: docs/quick-start.md
[3]: docs/featured-design.md
15 changes: 8 additions & 7 deletions build.gradle
Expand Up @@ -4,6 +4,7 @@ subprojects { project ->

repositories {
mavenCentral()
//jcenter()
}

apply plugin: 'checkstyle'
Expand All @@ -26,10 +27,10 @@ subprojects { project ->

buildscript {
repositories {
mavenCentral()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.3'
classpath 'com.android.tools.build:gradle:2.2.0'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}
Expand All @@ -39,7 +40,7 @@ ext {
minSdkVersion = 9
targetSdkVersion = 24
compileSdkVersion = 24
buildToolsVersion = '24.0.1'
buildToolsVersion = '24.0.2'
sourceCompatibilityVersion = JavaVersion.VERSION_1_7
targetCompatibilityVersion = JavaVersion.VERSION_1_7
}
Expand All @@ -48,10 +49,10 @@ ext.deps = [:]

// android
ext.deps.android = 'com.google.android:android:2.1.2'
ext.deps.supportv4 = 'com.android.support:support-v4:24.2.0'
ext.deps.supportAnnotations = 'com.android.support:support-annotations:24.2.0'
ext.deps.supportAppCompat = 'com.android.support:appcompat-v7:24.2.0'
ext.deps.supportDesign = 'com.android.support:design:24.2.0'
ext.deps.supportv4 = 'com.android.support:support-v4:24.2.1'
ext.deps.supportAnnotations = 'com.android.support:support-annotations:24.2.1'
ext.deps.supportAppCompat = 'com.android.support:appcompat-v7:24.2.1'
ext.deps.supportDesign = 'com.android.support:design:24.2.1'

// open source
ext.deps.javapoet = 'com.squareup:javapoet:1.7.0'
Expand Down
10 changes: 10 additions & 0 deletions docs/featured-design.md
@@ -0,0 +1,10 @@
# Featured design
Here is some rules and implementaiton details helping you to become familiar with the library and write cleaner code.
- Features must not access other features directly but shall always interoperate through feature callbacks. By following this rule you will be rewarded with simple, maintainable and testable code later on.
- You can define as many event callbacks as you need and they can have as many parameters as you need.
- Generated feature host class will contain a `dispatchOn<Event>()` method for each feature's `on<Event>()` method.
- To dispatch an event to all features you just need to call its corresponding `dispatchOn<Event>()`.
- It is allowed to call a `dispatchOn<AnotherEvent>()` method from a feature's `on<Event>()` callback. Feature host will make sure that currently running dispatch loop finishes and current event gets dispatched to all features before the new event gets dispatched.
- This make event dispatching to be asynchronous. It means you cannot assume that a `dispatchOn<Event>()` finishes, corresponding event has been delivered to all features. Actual event dispatching can happen also later in time. If you want to be notified after an event has been dispatched, you need to use `@FeatureEvent(dispatchCompleted = true)` and provide corresponding `OnDispatchCompleted` callback in `dispatchOn<Event>()` method. Provided callback will be notified after event dispatching finishes.
- Current implementation is intended to be used in MainThread. This is the only thread strategy implemented at this time.
- Featured is being actively developed and new library features are to be expected.
File renamed without changes
88 changes: 88 additions & 0 deletions docs/quick-start.md
@@ -0,0 +1,88 @@
# Quick Start

1) First you need to think of splitting your activity into independent components communicating via events. Start with standard activity or fragment lifecycle events like `onCreate()`, `onStart()` etc. and add other custome events like `onDataLoaded`, `onButtonClicked` etc. if required.

2) Create a basis feature class and declare all lifecycle events in there. Use `@FeatureEvent` annotation for events to be dispatched.

```java
public class SampleFeature extends Feature<SampleFeatureHost, Context> {
@FeatureEvent protected void onCreate(@NonNull CoordinatorLayout parent) {
// nop
}

@FeatureEvent protected void onStart() {
// nop
}

@FeatureEvent protected void onFabClicked() {
// nop
}

@FeatureEvent protected void onStop() {
// nop
}

@FeatureEvent protected void onDestroy() {
// nop
}
}
```

The `SampleFeature` class extends a parametrized `Feature` class. First parameter is the name of the feature host class to be generated. You can freely choose the name, but adding a `Host` string to the feature name is a good choice. Second parameter is a name of the context your feature is capable to access. It can be `Context`, `Activity`, `Fragment` or any other class you find usefull for your feature implementation. Later on, when you implement features you will be able to access this context from the feature code at any time.

Build the project. `SampleFeatureHost` class will be generated and source code will compile successfully.

3) Consider the `SampleFeature` class to be an interface defining feature lifecycle. Now you can implement your features by extending `SampleFeature` class as following:

```java
public class ToolbarFeature extends SampleFeature {
@Override protected void onFabClicked() {
// handle on-click-event here
}
}

public class FabFeature extends SampleFeature implements View.OnClickListener {
@Override public void onClick(View view) {
// dispatch on-click-event to other features
getFeatureHost().dispatchOnFabClicked();
}
}
```

Generated `SampleFeatureHost` class contains dispatch methods for each event method of the `SampleFeature`. Use these methods to dispatch events to your features. You can call those methods from the activity or from inside of any feature.

4) Register features to the feature host class in your activity or a fragment.
```java
public MyFragment extends Fragment {
private SampleFeatureHost mFeatureHost;

@Override public void onCreate(Bundle savedInstanceState) {
...
CoordinatorLayout parent = (CoordinatorLayout) findViewById(R.id.coordinator);

// create feature host and add a feature we created
mFeatureHost = new SampleFeatureHost(getContext())
.with(new FabFeature())
.with(new ToolbarFeature());

// dispatch event to all registered features
mFeatureHost.dispatchOnCreate(parent);
}

// dispatch other events correspondingly

@Override public void onStart() {
mFeatureHost.dispatchOnStart();
}

@Override public void onStop() {
mFeatureHost.dispatchOnStop();
}

@Override public void onDestroy() {
mFeatureHost.dispatchOnDestroy();
}
}
```

Your fragment become very simple and whole application code gets split into separated features with the very clean responcibilities. You can add new features or disable existing features depending on your use case or the device configuration etc. See `featured-sample` project for more code examples.
2 changes: 1 addition & 1 deletion featured-annotations/build.gradle
Expand Up @@ -23,7 +23,7 @@ configurations {
}

dependencies {
provided deps.android
//provided deps.android
compile deps.supportAnnotations
}

Expand Down
2 changes: 1 addition & 1 deletion featured-compiler/build.gradle
@@ -1,5 +1,5 @@
apply plugin: 'java'
apply plugin: 'checkstyle'
apply plugin: 'java'

sourceCompatibility = rootProject.ext.sourceCompatibilityVersion
targetCompatibility = rootProject.ext.targetCompatibilityVersion
Expand Down
@@ -0,0 +1,17 @@
package de.halfbit.featured.compiler;

import de.halfbit.featured.compiler.model.FeatureNode;

public class Assertions {

private Assertions() {
}

public static <S> S assertNotNull(S subj, FeatureNode featureNode) {
if (subj == null) {
throw new IllegalArgumentException("Subject is null in " + featureNode);
}
return subj;
}

}

0 comments on commit 5a5ae71

Please sign in to comment.