Opublikowano:
02.11.2023 - tagi:
Składnia Klasę w Swift tworzy się za pomocą słowa kluczowego class :
class MyClass {
var property1: String = ""
var property2: Int = 0
}
Tworzenie obiektów Stworzenie instancji klasy wygląda tak:
var myObject = MyClass ()
Dostęp do właściwości Za pomocą . możesz dostać się do właściwości obiektów:
myObject.property1 = "Hello"
myObject.property2 = 7
print (myObject.property1)
print (myObject.property2)
Metody Do klasy można dodać także funkcje:
class User {
var firstName: String = ""
var lastName: String = ""
func getFullName () -> String {
return "\(firstName) \(lastName) "
}
}
var user = User ()
user.firstName = "Jan"
user.lastName = "Kowalski"
print (user.getFullName())
Metody statyczne Możesz dodać do klasy metodę statyczną:
class MyClass {
static func sumUp (_ data : [Int ]) -> Int {
return data.reduce(0 , + )
}
}
let result: Int = MyClass .sumUp([1 , 2 , 3 ])
print (result)
Do metody statycznej można odwołać się tylko za pomocą nazwy klasy.
Konstruktor Tak samo jak w strukturach możesz za pomocą init przekazać dane dla tworzonej instancji:
class User {
var firstName: String
var lastName: String
init (firstName : String , lastName : String ) {
self .firstName = firstName
self .lastName = lastName
}
func getFullName () -> String {
return "\(firstName) \(lastName) "
}
}
var user = User (firstName: "Jan" , lastName: "Kowalski" )
print (user.getFullName())
Dziedziczenie Swift pozwala także na dziedziczenie z innej klasy.
class BaseClass {
var propA: Int = 0
func doSomething1 () {
print ("Call doSomething1 from BaseClass" )
}
}
class SubClass : BaseClass {
var propB: String = ""
func doSomething2 () {
print ("Call doSomething1 from SubClass" )
}
}
var object = SubClass ()
object.propA = 7
object.propB = "Some data"
object.doSomething1()
object.doSomething2()
Klasa pochodna dziedziczy wszystkie właściwości i metody po klasie bazowej.
W Swift dziedziczyć można tylko po jednej klasie.
Nadpisanie metody rodzica Jeśli klasa potomna używa takiej samej metody jak klasa bazowa (rodzic), to musisz użyć słowa kluczowego override .
class Shape {
func area () -> Double {
return 0.0
}
}
class Rectangle : Shape {
var a: Double = 0.0
var b: Double = 0.0
init (a : Double , b : Double ) {
self .a = a
self .b = b
}
override func area () -> Double {
return a * b
}
}
class Square : Shape {
var a: Double = 0.0
init (a : Double ) {
self .a = a
}
override func area () -> Double {
return a * a
}
}
var rect = Rectangle (a: 7.0 , b: 5.0 )
print (rect.area())
var square = Square (a: 10.0 )
print (square.area())
Nadpisanie właściwości klasy Jeśli klasa bazowa ma computed property możesz nadpisać ją w klasie potomnej.
class Product {
var price: Double {
return 0.0
}
}
class ProductA : Product {
override var price: Double {
return 500.0
}
}
class ProductB : Product {
override var price: Double {
return 1000.0
}
}
var productA = ProductA ()
var productB = ProductB ()
print (productA.price)
print (productB.price)
Możesz nadpisać także property observers .
Uwaga : Nie można nadpisać "zwykłych" właściwości klasy:
class A {
var counter = 1
}
class B : A {
override var counter = 2
}
Wywołanie metody rodzica Jeśli potrzebujesz wywołać w metodzie podklasy metodę należącą do rodzica, użyj słowa kluczowego super .
class ParentClass {
func doSomething () {
print ("ParentClass doSomething" )
}
}
class ChildClass : ParentClass {
override func doSomething () {
super .doSomething()
print ("ChildClass doSomething" )
}
}
var childObject = ChildClass ()
childObject.doSomething()
Za pomocą super można też dostać się do właściwości klasy.
Zapobieganie nadpisaniu Jeżeli nie chcesz, żeby właściwość lub metoda klasy były nadpisane użyj słowa kluczowego final .
class ParentClass {
final func doSomething () {
print ("ParentClass doSomething" )
}
}
class ChildClass : ParentClass {
override func doSomething () {
super .doSomething()
print ("ChildClass doSomething" )
}
}
var childObject = ChildClass ()
childObject.doSomething()
Przy takiej próbie zostanie zgłoszony błąd. final możesz użyć także do właściwości klasy.
Klasa to typ referencyjny Każdy obiekt klasy to typ referencyjny. To znaczy, że zmienna, do której przypisany jest obiekt, zawiera referencję do instancji, a nie samą instancję.
Przykład:
class User {
var firstName: String
var lastName: String
init (firstName : String , lastName : String ) {
self .firstName = firstName
self .lastName = lastName
}
func getFullName () -> String {
return "\(firstName) \(lastName) "
}
}
var user1 = User (firstName: "Jan" , lastName: "Kowalski" )
print (user1.getFullName())
var user2 = user1
print (user2.getFullName())
user1.firstName = "Grażyna"
user1.lastName = "Kowalska"
print (user2.getFullName())
Do zmiennej user2 przypisany został obiekt ze zmiennej user1 . A tak naprawdę do user2 została przypisana referencja do obiektu, którą przechowuje user1 . Więc zmiana danych poprzez user1 spowoduje, że user2 będzie wskazywać na te same dane.
Opublikowano:
31.10.2023 - tagi:
We październiku opublikowałem 7 wpisów :
React
React Testing Library: Jak mockować requesty? React Testing Library: Within React Testing Library: Jak sprawdzić kolejność wyświetlanych elementów na liście? React Testing Library: Jak pisać łatwe w utrzymaniu testy? JavaScript
Jak pobrać n pierwszych elementów tablicy? Swfit
Computed property Property Observers Przeczytałem trzy książki:
Facet jak młody bóg — Tadeusz OleszczukOutpost — Dmitry GlukhovskyWtorki z Morriem — Mitch AlbomPrzesłuchałem dwa audiobooki:
Strach — Jozef KarikaBohater wieków — Brandon SandersonOpublikowano:
26.10.2023 - tagi:
Czym są Property Observers? Swift pozwala na obserwowanie ewentualnych zmian przypisywanych do zmiennej. Dzięki property observers możesz zareagować na przypisanie wartości zmiennej tuż przed lub zaraz *po .
Możesz użyć:
willSet — Zostanie wywołane zaraz przed przypisanie nowej wartoścididSet — Zostanie wywołane tuż po przypisaniu wartościSkładania prezentuje się następująco:
var someProperty: Int = 0 {
willSet {
print ("New value: \(newValue) " )
}
didSet {
print ("Old value: \(oldValue) " )
}
}
Zarówno willSet jak i didSet przyjmują domyślnie parametry:
Dla willSet jest to newValue — jest to wartość, która ma zostać przypisana do zmiennej. Dla didSet jest to oldValue — jest to poprzednia wartość, którą zawierała ta zmienna. Możesz też sam określić nazwy tych parametrów:
var someProperty: Int = 0 {
willSet (valueToSet) {
print ("New value: \(valueToSet) " )
}
didSet (previousValue) {
print ("Old value: \(previousValue) " )
}
}
Przykład struct CaloriesCounter {
var totalCalories: Int = 0 {
willSet (newTotalCalories) {
print ("Total calories is \(newTotalCalories) " )
}
didSet {
if totalCalories > oldValue {
print ("Added \(totalCalories - oldValue) calories" )
}
}
}
}
Warto wiedzieć Jeśli oznaczysz zmienną jako property observer :
Nie może to być stała let , tylko var . Musi zawierać wartość domyślną. Jeśli nie wiesz, co przypisać przypisz nil . Możesz też oznaczyć zmienną jako optional — wtedy domyślnie zostanie przypisane nil do zmiennej. Nie możesz użyć tego wspólnie z computed property . Ponieważ computed property nie przechowuje wartości. Nie musisz używać willSet i didSet razem. Jeśli potrzebujesz tylko jednej z nich, użyj tylko tej. Opublikowano:
19.10.2023 - tagi:
Czytelność testów "Kod częściej się czyta, niż pisze", "Kod nie jest zapisany w skale". Te spostrzeżenia odnoszą się także do testów. W końcu testy to też kod!
W takim razie warto zastanowić się, jak można zwiększyć ich czytelność?
Nie tylko czytelność. Jak pisać testy łatwiejsze w utrzymaniu?
Lepsze testy — krok po kroku Zacznijmy od przykładu.
Komponent:
export function MyComponent ( ) {
const [state, setState] = useState (0 );
return (
<>
<div>
{state}
</div>
<div>
<button onClick={() => setState((prevState: number) => prevState + 1)}>Increment</button>
<button onClick={() => setState((prevState: number) => prevState - 1)}>Decrement</button>
</div>
</>
)
}
Lepsze testy: Krok 1 const { getByRole, getByText } = screen;
test ('should increment counter' , async () => {
render (<MyComponent />);
const btnIncrement = getByRole ('button' , { name : "Increment" });
await userEvent.click (btnIncrement);
expect (getByText ('1' )).toBeInTheDocument ();
});
test ('should decrement counter' , async () => {
render (<MyComponent />);
const btnDecrement = getByRole ('button' , { name : "Decrement" });
await userEvent.click (btnDecrement);
expect (getByText ('-1' )).toBeInTheDocument ();
});
Co można zrobić, żeby test był bardziej czytelny?
Lepsze testy: Krok 2 Za każdym razem trzeba wywoływać funkcję: render() .
Wrzućmy ją do funkcji: renderComponent :
const renderComponent = ( ) => render (<MyComponent />);
Refaktoring testu:
test ('should increment counter' , async () => {
renderComponent ();
const btnIncrement = getByRole ('button' , { name : "Increment" });
...
});
test ('should decrement counter' , async () => {
renderComponent ();
const btnDecrement = getByRole ('button' , { name : "Decrement" });
...
});
Jest odrobinę lepiej.
Czas na następny krok.
Lepsze testy: Krok 3 Żeby testować element, potrzebujesz referencji do elementów znajdujących się w komponencie.
Pisanie zapytać w stylu:
const btnIncrement = getByRole ('button' , { name : "Increment" });
Jest męczące.
A co gdyby funkcja renderująca testowany komponent (renderComponent ) zwracała referencje do elementów, które posiada?
Nowa wersja renderComponent :
const renderComponent = ( ) => {
render (<MyComponent />);
const btnIncrement = getByRole ('button' , { name : "Increment" });
const btnDecrement = getByRole ('button' , { name : "Decrement" });
const userClicksOn = async (element ) => await userEvent.click (element);
return {
btnIncrement,
btnDecrement,
userClicksOn
}
}
I zaktualizowane testy:
test ('should increment counter' , async () => {
const { btnIncrement, userClicksOn } = renderComponent ();
await userClicksOn (btnIncrement);
expect (getByText ('1' )).toBeInTheDocument ();
});
test ('should decrement counter' , async () => {
const { btnDecrement, userClicksOn } = renderComponent ();
await userClicksOn (btnDecrement);
expect (getByText ('-1' )).toBeInTheDocument ();
});
Podsumowanie Takie podejście do pisania testów sprawia, że rozwiązujesz kilka problemów:
Centralizacja — Ewentualne zmiany, będziesz robić w funkcji renderującej komponent. Nie musisz "skakać" po wielu testach, żeby coś naprawić. Testy są bardziej czytelne. Opublikowano:
17.10.2023 - tagi:
Jaka kolejność na liście? Można to sprawdzić na kilka sposobów.
Najpierw kod przykładowej aplikacji:
import { useState } from "react" ;
export function MyComponent ( ) {
const [tasks, setTasks] = useState (['Task 1' , 'Task 2' , 'Task 3' ]);
const [task, setTask] = useState ('' );
const onChangeTask = (e ) => {
setTask (e.target .value )
};
const onAddTask = (e ) => {
e.preventDefault ();
setTasks ([task, ...tasks]);
setTask ('' );
}
return (
<>
<form onSubmit={onAddTask}>
<label htmlFor="newTask">Task title:</label>
<input type="text" id="newTask" value={task} onChange={onChangeTask} />
<button>Add</button>
</form>
<ul>
{
tasks.map((task, index) => (
<li key={index}>
{task}
</li>
))
}
</ul>
</>
)
}
Przykłady Sposób 1: toMatchInlineSnapshot Jest udostępnia funkcję toMatchInlineSnapshot testowania struktury DOM.
import { render, screen } from '@testing-library/react' ;
import { MyComponent } from "./MyComponent" ;
test ('should check order of list items' , async () => {
const { getByRole } = screen;
render (<MyComponent />);
const list = getByRole ('list' );
expect (list).toMatchInlineSnapshot (`
<ul>
<li>
Task 1
</li>
<li>
Task 2
</li>
<li>
Task 3
</li>
</ul>
` )
});
Jeśli element listy jest dość rozbudowany takie testowanie może być uciążliwe. Ale można to obejść w prosty sposób!
Wywołaj najpierw test z toMatchInlineSnapshot bez żadnych argumentów:
expect (list).toMatchInlineSnapshot ();
po chwili zostanie dodany argument do toMatchInlineSnapshot z listą elementów.
Sposób 2: Tablica elementów Możesz też pobrać nazwy elementów i umieścić je w tablicy:
import { render, screen } from '@testing-library/react' ;
import { MyComponent } from "./MyComponent" ;
test ('should check order of list items' , async () => {
const { getAllByRole } = screen;
render (<MyComponent />);
const listItems = getAllByRole ('listitem' ).map ((item ) => item.textContent );
expect (listItems).toEqual (['Task 1' , 'Task 2' , 'Task 3' ]);
});
Nowsze wpisy Poprzednie wpisy