najważniejsze podobieństwa

twins-small

omówię:

- klasy
- silne typowanie
- enumy
- interfejsy
- wstrzykiwanie zależności
- kolekcje

klasy

Klasa to jedna z podstawowych konstrukcji jeżeli chodzi o Javę i TS.

Jakie wspólne elementy posiada klasa w TS które znasz z Javy?

– konstruktor

– zmienne

– metody/funkcje

– operatory zasięgu 

Spójrz na poniższy kod. 

export class Car {
public brand: string;
public model: string;
public engineType: EngineType;
public type: CarType;

constructor(brand: string,
model: string,
engineType: EngineType,
type: CarType) {
this.brand = brand;
this.model = model;
this.engineType = engineType;
this.type = type;
}
public getBrand(): string {
return this.brand;
}

public setBrand(brand: string): void {
this.brand = brand;
}

public getModel(): string {
return this.model;
}

public setModel(model: string): void {
this.model = model;
}

public getEngineType(): EngineType {
return this.engineType;
}

public setEngineType(engineType: EngineType): void {
this.engineType = engineType;
}

public getType(): CarType {
return this.type;
}

public setType(type: CarType): void {
this.type = type;
}
}

Tak mogłaby wyglądać klasa, która reprezentuje obiekt samochodu. Powyższy kod będzie działał poprawnie natomiast nie jest najbardziej optymalny oraz zgodny z powszechnie panującymi standardami.

Obecnie programowanie idzie w kierunku szybszego pisania oraz wszelkiego sugar syntaxu. W Javie możesz zastosować Lombok’a i zapewne wiele razy z niego korzystałeś. To co uzyskujesz dzięki tej bibliotece to m.in. mniej kodu boilerplate. Dzięki takiemu podejściu możesz się skupić na tym jak dany kod wpływa na rozwiązanie biznesowe a nie na pisaniu getterów i setterów. Type Script dostarcza takiej możliwości bez potrzeby dodawania zewnętrznych bibliotek. 

Ważną różnicą między Javą a Type Scriptem jest dostęp do zmiennych w klasie. Type Script dopuszcza odwołanie do zmiennych obiektu poprzez nazwę zmiennej np. this.car.model

Spójrz poniżej jak uprościć klasę Car

export class Car {
constructor(public brand: string,
public model: string,
public engineType: string,
public type: string) {}
}

Jak widzisz klasa po zmianach wygląda jak ta w Javie gdy dodamy do niej adnotacje Lombokowe. 

Ważne aby zmienne w konstruktorze były oznaczone jako public. Dzięki temu będziesz mógł się do nich odnieść za pomocą nazwy.

Użycie klasy car.model.ts w app.component.ts

import { Component } from '@angular/core';
import { Car } from './car/car.model';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
public car: Car;
public car1: Car;

constructor() {
this.car = new Car('Audi', 'A6', 'benzyna', 'sedan');
this.car1 = new Car('Mercedes', 'E', 'diesel', 'coupe');

const carToString =
`${this.car.brand}, ${this.car.model}, ${this.car.type}, ${this.car.engineType}`
}
}

Do zapamiętania:

– w Type Scripcie aby dostać się do zmiennych nie potrzebujesz getterów i setterów, aczkolwiek gettery i settery także występują. 

– w Type Scripcie klasa może posiadać tylko jeden konstruktor. 

enumy

Enumy to kolejna przydatna konstrukcja. W TS enumy są nieco uproszczone względem Javy.

Spójrz na poniższy przykład.

export enum EngineType {
DIESEL,
GASOLINE,
}

Pozornie wszystko wydaje się niemal identyczne jak w Javie. Jeżeli spróbujesz wyświetlić wartość jaka kryje się w poszczególnych elementach to DIESEL będzie miało wartość „0”, natomiast GASOLINE wartość „1”. Domyślnie przypisywane są numery od 0 do n. Poprawnie jest nadać wartość elementowi

export enum EngineType {
DIESEL = 'DIESEL',
GASOLINE = 'GASOLINE'
}

W tym momencie wartości będą takie jak przypisałeś po prawej stronie znaku równości.

Jeżeli chodzi o enumy to nie ma tutaj za wiele do dodania.

interfejsy

Interfejsy są bardzo ważną konstrukcją zarówno w Javie jak i Type Scriptcie. Pewne właściwości są wspólne jak deklaracje metod oraz implementacja interfejsu w klasie. Istnieją także różnice jak m.in. odpowiedź z backendu jest mapowana właśnie na interfejs a nie na klasę. 

Proces mapowania oraz pobierania danych z backendu prezentuję w materiałach w kroku: Praktyka.

Spójrz na wykorzystanie interfejsu czyli dodanie definicji metody oraz jej nadpisanie. Pokażę Ci to na klasycznym przykładzie zwierząt. 

Wszystkie pliki stwórz w katalogu app.

Dodaj plik pet.interface.ts

export interface Pet {
sound(): string;
}

dodaj także cat.ts

import {Pet} from './pet.interface';

export class Cat implements Pet {
sound(): string {
return 'Miau Miau';
}
}

dog.ts

import {Pet} from './pet.interface';

export class Dog implements Pet {
sound(): string {
return 'Hau Hau';
}
}

w pliku app.component.ts dodaj poniższy kod

import {Component} from '@angular/core';
import {Pet} from './pet.interface';
import {Dog} from './dog';
import {Cat} from './cat';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
public pets: Pet[] = [new Dog(), new Cat()];
}

oraz w app.component.html

<div *ngFor="let pet of pets">
{{pet.sound()}}
</div>

Powinieneś zobaczyć następujący widok.

kolekcje

Zanim przejdziemy do wtrzykiwania zależności oraz zanim wytłumaczę Ci czym jest serwis, omówię kolekcję. Zaczniemy od kolekcji ponieważ w kolejnym punkcie wykorzystamy właśnie mapę oraz listę.

Kolekcje nie są aż tak rozbudowane jak w Javie. W Type Script są dostępne trzy rodzaje: lista, mapa oraz set.

Spójrz na poniższy kod

import { Injectable } from '@angular/core';

@Injectable(
{
providedIn: 'root'
}
)
export class CarService {

private carsWithoutDiscount = ['A8', 'Seria 7', 'Klasa S'];
private carsDiscount: Map<string, number> = new Map([
['A6', 20],
['Klasa E', 15],
['Seria 5', 17],
]);

getCarDiscount(carModel: string): number {
return this.carsDiscount.get(carModel);
}

getCarsWithoutDiscount(): string[] {
return this.carsWithoutDiscount;
}
}

W Type Scripcie lista oraz tablica są ze sobą tożsame. Jak widzisz jest to bardzo podobna konstrukcja jak tablice w Javie.

Mapy także są bardzo podobne do tych, które znasz z Javy. Uważam, że są to dla Ciebie na tyle intuicyje konstrukcje, że nie ma sensu za bardzo się o nich rozpisywać i ten prosty przykład wykorzystania wystarczy.

wstrzykiwanie zależności

W Angularze także spotkasz się z pojęciem wstrzykiwania zależności. Nie różni się ono zbytnio od podejścia, które znasz ze Springa. Wstrzykiwanie zależności jest realizowane poprzez konstruktor. Aby klasa była możliwa do wstrzyknięcia musi zostać oznaczona adnotacją @Injectable().  Z reguły klasy z adnotacją @Injectable() są serwisami oraz posiadają w nazwie słowo kluczowe service. Można to pokazać na prostym przykładzie.

Stwórz serwis dla samochodów z metodą która sprawdza czy dana marka pojazdu posiada zniżkę. 

W katalogu app stwórz plik car.service.ts oraz dodaj w pliku następujący kod:

import { Injectable } from '@angular/core';

@Injectable(
{
providedIn: 'root'
}
)
export class CarService {

private carsWithoutDiscount = ['A8', 'Seria 7', 'Klasa S'];
private carsDiscount: Map<string, number> = new Map([
['A6', 20],
['Klasa E', 15],
['Seria 5', 17],
]);

getCarDiscount(carModel: string): number {
return this.carsDiscount.get(carModel);
}

getCarsWithoutDiscount(): string[] {
return this.carsWithoutDiscount;
}
}

Czym jest dziwnie wyglądający zapis providedIn: root w adnotacji? Jest to mechanizm który pozwala zakomunikować Angularowi, żeby udostępnił ten serwis dla całej aplikacji automatycznie bez potrzeby dodawania go do głównego modułu app.module.ts.

Więcej na temat architektury znajdziesz w kolejnym kroku procesu: Teoria.

Przejdź do pliku app.component.ts

import {Component, OnInit} from '@angular/core';
import {CarService} from './car/car.service';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
public discount = 0;

constructor(private carService: CarService) {
}

ngOnInit(): void {
this.discount = this.carService.getCarDiscount('A6');
}
}

oraz w pliku app.component.html dodaj kod

<p>Przysługuje {{ discount }}% zniżki na Audi A6.</p>

<p>Modele bez zniżki:</p>
<div *ngFor="let car of carsWithoutDiscount">
{{car}}
</div>

Gdy przejdziesz do aplikacji powinieneś zobaczyć następujący widok.