Everything is changing very fast!
JQuery, Tapestry, Flash, Flex, Backbone, ie...
AngularJS
Angular, Android
React, Stencil, Flutter, ie... :(
Following the SOLID principles :
Building software designs...
More understandable
Easier to maintain
Easier to extend
A list of commonly used patterns ;)
Starting by a well known structural pattern...
A long, long time ago...
Angular :
View
Controller
Model
HTML Template
Component
Service
React :
View
Controller
Model
View
<div>
<app-user-search (changed)="onCriteriaChanged($event)">
</app-user-search>
<app-users-list [users]="users$ | async"
(select)="onSelectUser($event)">
</app-users-list>
</div>
"View"
@Component({
templateUrl: './main-page.component.html',
styleUrls: ['./main-page.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MainPageComponent implements OnInit {
users$: Observable<User[]>;
constructor(private userService: UserService) {}
ngOnInit() {
this.users$ = this.userService.fetchUsers();
}
}"Controller"
@Injectable()
export class UserService {
constructor(@Inject(USER_API_URL) private urlUsers: string,
private http: HttpClient) {}
fetchUsers(criteria: UserCriteria): Observable<User[]> {
return this.http.get<User[]>(this.urlUsers);
}
}"Model"
Other structuring patterns :
MVP (Model - View - Presenter)
Other structuring patterns :
MVVM (Model - View - ViewModel)
MVVM Android :
class ViewModelActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// Obtain the ViewModel component.
UserModel userModel = ViewModelProviders.of(getActivity())
.get(UserModel.class)
// Inflate view and obtain an instance of the binding class.
val binding: UserBinding =
DataBindingUtil.setContentView(this, R.layout.user)
// Assign the component to a property in the binding class.
binding.viewmodel = userModel
}
}MVVM Android :
<CheckBox
android:id="@+id/rememberMeCheckBox"
android:checked="@{viewmodel.rememberMe}"
android:onCheckedChanged="@{() -> viewmodel.rememberMeChanged()}" />Asynchronous data streams
Everything is a stream :
Declarative approach using :
Pure functions
Iterator pattern
const products$: Observable<Product[]> =
fromEvent(this.input.nativeElement, 'keyup')
.pipe(
map(event => event.target.value),
filter((search: String) => search.length > 2),
debounceTime(250),
distinctUntilChanged(),
switchMap(search => this.fetchProducts(search))
);
ReactiveX implementations :
RxJS
RxJava
RxKotlin
RxDart
RxPython
...
Common uses :
Angular HttpClient, ReactiveForms...
Android LiveData
Flutter FutureBuilder/StreamBuilder
Heavily depends on...
Event handling :
Observers
Subjects
Event handling :
Subject :
Observer :
registerObserver(observer)
unregisterObserver(observer)
notifyObservers()
notify()
Main goal :
Single instance
Coordinate actions across the system
Perfect to store data
Lazy construction
Angular Services
Business specific
Transverse
@Injectable()
export class UserService {
constructor(@Inject(USER_API_URL) private urlUsers: string,
private http: HttpClient) {}
fetchUsers(): Observable<User[]> {
return this.http.get<User[]>(this.urlUsers);
}
}Angular Services
Easy to use...
Thanks to Dependency Injection
@Component({
templateUrl: './detail-page.component.html',
styleUrls: ['./detail-page.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class DetailPageComponent implements OnInit {
users$: Observable<User[]>;
constructor(private userService: UserService) {}
ngOnInit() {
this.users$ = this.userService.fetchUsers();
}
Angular Services
Best place to store business data
@Injectable()
export class UserService {
private _users$: Observable<User[]>;
get users$(): Observable<User[]> {
return this._users$;
}
constructor(@Inject(USER_API_URL) private urlUsers: string,
private http: HttpClient) {
this._users$ = this.http.get<User[]>(this.urlUsers).pipe(
shareReplay()
);
}
}Redux Store
Testability
Dependency hiding
Concurrency
Creational pattern
One interface and several implementations
Using the Right implementation for a given case
Generally used with a DI mechanism :
Angular services
Android libraries (Dagger 2, Koin)
Flutter libraries (Inject)
Angular Injection Tokens :
// user.service.ts
export const USER_SERVICE =
new InjectionToken<UserService>('USER_SERVICE');
export interface UserService {
fectchAll(): Observable<User[]>;
fectchOne(): Observable<User>;
}Angular module :
// app.module.ts
@NgModule({
declarations: [AppComponent],
imports: [
...
],
providers: [
{ provide: USER_SERVICE, useClass: UserServiceHttpImpl },
...
],
bootstrap: [AppComponent]
})
export class AppModule {}Angular Environments
// app.module.ts
@NgModule({
...
providers: [
{ provide: USER_SERVICE, useClass: environment.userServiceImpl },
...
]
})
export class AppModule {}// environment.ts
{
userServiceImpl: UserServiceHttpImpl
}Encapsulates a family of algorithms
Makes them interchangeable.
In a component architecture :
Dumb - Smart components
// detail-page.component.ts
<div class="user-detail-content" *ngIf="user$ | async as user">
<h1>{{ user.firstName }}</h1>
<app-user-form [user]="user" (validate)="onValidate($event)">
</app-user-form>
</div>
The new standard in front development?
Declarative API to define UI components
export default class App extends Component {
...
render() {
return (
<div>
<Header />
<List items={this.state.items} />
<Footer />
</div>
);
}
}More than just "UI" components :
@override
Widget build(BuildContext context) => FutureBuilder<Restaurant>(
future: restaurantService
.fetchRestaurantDetail(context: context, restaurant: restaurant)
.then((restaurant) => restaurantService.fetchRestaurantRatings(
restaurant: restaurant,
limit: 4
)),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return snapshot.hasData
? RestaurantWidget(restaurant: snapshot.data)
: AppProgressIndicator();
});At last on Android :
Android Jetpack Compose
class ComposeActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent { CraneWrapper { MyApp() } }
}
@Composable
fun MyApp() {
MaterialTheme {
Text(
text = "Hello world!",
style = +themeTextStyle { h3 }
)
}
}
}I have developed a components library and want to implement a "disabled" and "tabIndex" state on all of them... ?
Solves a common Class limitation :
Single Super Class
Share a common behavior between different classes
Mixins can be done in Typescript
But it can be a bit tricky
Used in some component libraries (Angular Material) to define common behavior such as "color", "disabled"...
Well implemented in Dart 2.X :
mixin Coder {
void code() {
print("Trying to use design patterns...");
}
}
class Laurent extends Person with Coder {}
void main() {
const laurent = Laurent();
laurent.code();
}With interface default implementations in Kotlin :
interface Developer {
fun code() {
...
}
}
class UserCodeEnSeine : User, Developper {
...
}React hooks are Mixins done right :
function Example() {
const [count, setCount] = useState(0);
return (
<div>
<p>Vous avez cliqué {count} fois</p>
<button onClick={() => setCount(count + 1)}>
Cliquez ici
</button>
</div>
);
}Patterns are the first thing to learn.
Frameworks use them to solve common software development problems.
Their implementation differs, but the logic remains the same.