Coding tips: Interfețe

Un concept cu care am avut ceva de furcă la început a fost folosirea interfețelor. Peste tot găseam o explicație vag sumară, eventual și ceva legat de un contract și rămâneam cel puțin la fel de confuz ca înainte.

Din punctul meu de vedere, dacă nu plănuiești să ai un cod extensibil (e.g. prin plugin-uri), interfețele sunt overkill. Nu strică, dar nici nu aduc valoare.

Conceptele SOLID sunt another can of worms, despre care probabil voi scrie cu altă ocazie.

Pentru a înțelege scopul interfețelor, este util să înțelegi conceptele SOLID, în special open-closed principle, care zice că un obiect ar trebui să permită extinderea, nu modificarea.

Un exemplu clasic și practic este reprezentat de un calculator de suprafețe a diferitelor obiecte geometrice. De exemplu, să zicem că avem o clasă simplă ce calculează suprafața unui pătrat:

class Suprafata
{
  public function patrat($latura)
  {
    return $latura * $latura;
  }
}

Simplu, eficient, dar ce se întâmplă dacă avem nevoie să calculăm și un dreptunghi? Păi… modificăm clasa:

class Suprafata
{
  public function patrat($latura)
  {
    return $latura * $latura;
  }

  public function dreptunghi($lungime, $latime)
  {
    return $lungime * $latime;
  }
}

Ce ne zice zice principiul Open-Closed? Că un obiect – o clasă în acest caz – trebuie extins, nu modificat. Deci noi tocmai am încălcat acest principiu!

Ce se întâmplă dacă vrem să calculăm și suprafața unui cerc? Sau a unui cilindru? Sau a unei piramide? Va trebui să modificăm clasa de fiecare dată!

În plus, SRP – primul principiu din acronimul SOLID – este și el încalcat. Principiul ăsta zice că o clasă trebuie să aibă un singur motiv pentru care este modificată. Cum putem repara problema?

În primul rând, extragem logica necesară calcului într-o clasă separată:

class Patrat
{
  public function __construct($latura)
  {
    $this->latura = $latura;
  }

  public function calculeazaSuprafata()
  {
    return $this->latura * $this->latura; 
  }
}

(și facem același lucru și pentru dreptunghi, cerc și tot ce mai avem noi nevoie)

Apoi modificăm clasa inițială să accepte o instanță ce calculează ce avem noi nevoie:

class Suprafata
{
  public function calculeaza($obiectGeometric)
  {
    if (method_exists($obiectGeometric, 'calculeazaSuprafata')) {
      return $obiectGeometric->calculeazaSuprafata();
    }
  }
}

Chestia asta funcționează și am împușcat doi iepuri dintr-un foc: bifăm OCP și SRP. Dar e o problemă: trebuie să verificăm dacă putem apela metoda aia (method_exists($obiectGeometric, 'calculeazaSuprafata')). Oare există o modalitate mai elegantă? Desigur!

Interludiu: Contracte

Un contract în viața reală presupune o înțelegere între două părți. De ce n-ar însemna același lucru si în programare? Putem face un contract între clasa Suprafata și clasa Patrat, astfel încât clasa Patrat promite că va avea întotdeauna o metodă numită calculeazaSuprafata.

Acest contract este numit Interfață și conține doar semnăturile metodelor:

interface ObiectGeometric
{
  public function calculeazaSuprafata();
}

Semnătura metodei presupune că va fi implementată întocmai. Dacă metoda are argumente în interfață, trebuiesc adăugate aceleași argumente și în implementare.

Modificăm clasa Patrat, astfel încât să implementeze metodele interfeței (i.e. se angajează să respecte contractul):

class Patrat implements ObiectGeometric
{
//....

Și modificăm și clasa Suprafata astfel încât acceptă doar implementări ale acestei interfețe

class Suprafata
{
  public function calculeaza(ObiectGeometric $obiectGeometric)
  {
    return $obiectGeometric->calculeazaSuprafata();
  }
}

Treaba asta, cu un cuvânt înainte de parametru( function calculeaza(ObiectGeometric $obiectGeometric)) se numește Type Hinting și va obliga metoda să accepte doar instanțe ale acelei clase – sau implementări, în cazul nostru.

Ca type hinting poți folosi numele unei clase, al unei interfețe sau al unei primitive a limbajului (e.g. Array, String). Eu am ajuns la concluzia că nu are sens să folosești numele unei clase, ci doar numele unei interfețe.


Una dintre cele mai mari greșeli

Când am descoperit prima dată interfețele, am zis că e musai să definesc toate metodele dintr-o clasă, indiferent că-s publice sau private. Practic aveam o oglindire 1:1 a clasei în interfață! Ceea ce, evident, este greșit.

De fapt, o interfață definește doar metode publice ce sunt apelate dintr-o altă clasă. De exemplu, în WordPress există conceptul de hooks, ce ne obligă să avem metode publice:

add_action( 'foo', [$instanta, 'numeMetoda'] );

Aceste metode nu trebuiesc definite în interfață, pentru că sunt specifice acelei implementări.

Data viitoare voi scrie despre clasele abstracte.

De la Windows la Ubuntu si inapoi

De când mă știu a sta în fața unui computer am folosit Windows. Când am decis că vreau să devin programator am făcut trecerea către sisteme Linux. Mi-aș fi dorit un Mac pe vremea aceea dar cum nu îmi permiteam așa ceva am ales soluția cea mai apropriată. Se auzeau zvonuri despre Windows 9 când am început să folosesc ElementaryOs.

Mă așteptam să trec printr-o perioadă mai lungă de acomodare. Aveam impresia că voi fi nevoit să folosesc terminalul și că îmi va fi greu, că îmi va lipsi interfața grafică a Windows-ului. Dar am descoperit repede că terminanul nu înlocuiește componenta grafică a sistemului de operare ci o completează.

Pe atunci foloseam dual boot, lucram des cu Adobe Illustrator și After Effects și nu mă descurcam cu alternativele oferite pe Linux. Programele oferite de Adobe erau singurele care mă țineau captiv în lumea Windows.

Dar am început să lucrez cu RaspberryPi așa că am făcut alegerea logică de a folosi mai des Ubuntu și nu am fost dezamăgit deloc de alegere:

  • arată și se mișcă bine, consumă puține resurse;
  • ușor de costumizat și multe skin-uri disponibile;
  • terminal-ul este foarte util, competent și oferă un workflow mult mai bun pentru dezvoltare;
  • Inkscape ca alternativă bună pentru Illustrator;
  • Blender ca alternativă bună pentru After Effects;
  • flexibilitatea sistemului de operare mi-a permis dezvoltarea unor periferice proprii;
  • este gratuit!;

Nu îmi lipsea nimic din mediul Windows. Am înlocuit și suita Adobe cu programe open-source iar de gaming nu se pune problema (nu sunt gamer, dar la nevoie aveam Minecraft și Steam).

Toate beneficiile și costuri 0. Așa am renunțat la Windows și pentru calculatorul de acasă și pentru cel de la muncă.

Doar că mi-am schimbat jobul, și am primit un laptop cu Windows 10. Ce să zic? „Bine te-am găsit, ce știi să faci?”. Observ că îmi lipsește UX-ul din Ubuntu, Windows face niște chestii ciudate:

  • alege să iși facă update în cele mai proaste momente;
  • nimic nu se aproprie de terminal-ul linux (cmd este doar o fosilă, power shell e ciudat). Alternativele sunt mai bune ,Git Bash de exemplu, dar nici ele nu vin cu o experiență bună out of the box, tot trebuiesc configurate;
  • uneori ferestre noi apar cu bara de titlu (partea cu – [] x) în afara suprafeței de lucru. Nu am cum să mut fereastra așa că trag de unul din colțurile de jos ca ele să pice bine în desktop;
  • dacă m-am deconectat de la un monitor extern multe ferestre se deschid în acel spațiu ce acum îmi este inaccesibil. Chiar dacă resetez modul de proiectie (win+p) sau dau restart, ele tot acolo se deschid;
  • pentru Netflix folosesc un monitor extern cu niste boxe atașate de acesta. Cu Windows se întampla de multe ori să nu am sunet prin hdmi, trebuie sa urmez un ritual anume cu scos-bagat mufa de hdmi și schimbat sursa audio-out până funcționează;
  • nu am explorat prea mult, dar pare că windows nu este atât de personalizabil;
  • au revenit probleme precum: viruși și instalarea unui antivirus, vulnerabilități , privacy, BSOD, „google chrome has stopped working”;
  • pentru unele aplicații textul este randat ciudat din cauza problemor cu dpi;
  • sunt obișnuit să pornesc IDE direct în folder din linia de comandă. Pe Windows comanda pentru subline subl . deschide mai multe sesiuni de sublime … (later edit: am aflat din comentarii că acesta defapt este un feature);

Singurele motive pentru care Windows merită folosit: suita Office și programele VPN. Noroc că există VirtualBox cu guest additions!

Compilează Libvirt pentru Python pe Ubuntu

Să instalezi Libvirt pe Ubuntu pentru Python e simplu:

sudo apt install -y python python-pip libvirt-dev
pip install libvirt-python

Cu instalarea default, ar putea să-ți lipsească unele bindings ale API-ului Libvirt expuse pachetului Python, deși ai ultima versiune.  Dacă vreodată ai nevoie să compilezi librăria, uite cum poți (setup-ul meu a fost Libvirt 4.0.0 pe Ubuntu 16.04 cu Python 2.7):

#!/usr/bin/env bash

WORK_DIR="/tmp/libvirt"

sudo apt update
sudo apt install -y git

# LIBVIRT

WORK_DIR_LIBVIRT="$WORK_DIR/libvirt"
mkdir -p $WORK_DIR_LIBVIRT
cd $WORK_DIR_LIBVIRT

LIBVIRT_VERSION="v4.0.0"
git clone -b $LIBVIRT_VERSION --single-branch --depth 1 https://github.com/libvirt/libvirt.git .
git checkout $LIBVIRT_VERSION

sudo apt install -y \
  gettext \
  libtool \
  autoconf \
  autopoint \
  pkg-config \
  xsltproc \
  libxml2-utils
./bootstrap

sudo apt install -y \
  libnl-3-dev \
  libnl-route-3-dev \
  libxml2-dev \
  libdevmapper-dev \
  libpciaccess-dev \
  python
./configure

sudo apt install -y intltool
aclocal

make
sudo make install

# LIBVIRT PYTHON

WORK_DIR_LIBVIRT_PYTHON="$WORK_DIR/python"
mkdir -p $WORK_DIR_LIBVIRT_PYTHON
cd $WORK_DIR_LIBVIRT_PYTHON

LIBVIRT_PYTHON_VERSION="v4.0.0"
git clone -b $LIBVIRT_PYTHON_VERSION --single-branch --depth 1 https://github.com/libvirt/libvirt-python .
git checkout $LIBVIRT_PYTHON_VERSION

sudo apt install -y python-dev

python setup.py build
python setup.py install

# CLEANUP

rm -r $WORK_DIR
sudo apt purge -y \
  gettext \
  libtool \
  autoconf \
  autopoint \
  pkg-config \
  xsltproc \
  libxml2-utils \
  libnl-3-dev \
  libnl-route-3-dev \
  libxml2-dev \
  libdevmapper-dev \
  libpciaccess-dev \
  intltool \
  python-dev

Cele de mai sus pot varia în funcție de configurația ta de Ubuntu. Doar fii atent la erorile privind tool-urile care lipsesc.

Acum poți verifica simbolurile de care aveai nevoie:

sudo apt install -y binutils
nm -g /usr/local/lib/libvirt.so

Recomand ca tentativele de compilare să fie într-un mediu izolat.

Selectarea categoriilor cu ajutorul Carbon Fields

Despre Carbon Fields am scris atât pe blogul personal, cât și pe forum și am tot început să-l folosesc la diverse proiecte, având tot felul de provocări, care mai de care mai interesantă. Ultima? Să pot permite selectarea unei singure categorii pentru un anumit post. Soluția e foarte simplă și are câțiva pași simpli. Evident, în loc de category se poate folosi și o taxonomie proprie.

<?php

use \Carbon_Fields\Container;
use \Carbon_Fields\Field;

add_action('after_setup_theme', function () {
  // inițializăm Carbon Fields
  \Carbon_Fields\Carbon_Fields::boot();
});

function getCurrentValue()
{
  if (!is_admin()) {
    // pentru că toate câmpurile sunt parsate și în frontend, nu facem interogările decât în admin
    return [];
  }

  $currentPostID = absint($_GET['post'] ?? 0);

  $mediumTerm = wp_list_pluck((array) get_the_terms($currentPostID, 'category'), 'term_id');
  
  // în cazul în care postul are deja mai multe categorii setate, o întoarcem doar pe prima
  return array_shift($mediumTerm);
}

function getAvailableOptions()
{
  if (!is_admin()) {
    return [];
  }
  $terms = get_terms([
    'taxonomy' => 'category',
    'hide_empty' => false,
  ]);

  $parsedTerms[-1] = __('Select');

  foreach ($terms as $term) {
    // generăm un array de forma "id" => "Nume", pentru a-l putea afișa în select 
    $parsedTerms[$term->term_id] = $term->name;
  }

  return $parsedTerms;
}

add_action('carbon_fields_register_fields', function () {
  Container::make('post_meta', __('My Category'))
    ->where('post_type', '=', 'post')
    ->add_fields([
      Field::make('select', 'my_category', __('Category'))
        ->add_options(getAvailableOptions())
        ->set_required(true),
    ]);
});


add_action('carbon_fields_post_meta_container_saved', function ($postID) {
  $term = absint(carbon_get_the_post_meta('my_category'));
 

  if (!empty($term)) {
    wp_set_post_terms($postID, [$term], 'category', false);
    // https://developer.wordpress.org/reference/functions/wp_set_post_terms/
    // salvăm toată povestea
    // al patrulea argument, `false` poate fi setat ca `true` dacă se dorește 
    // adăugarea mai multor categorii
  }
});


add_filter('register_taxonomy_args', function ($args, $taxonomy) {
  if ($taxonomy === 'category') {
    // ascundem selectorul de categorie
    $args['meta_box_cb'] = false;
  }
  return $args;
}, 10, 3);

Cum configurezi eficient Windows pentru PHP Dev?

Mi se întâmplă deseori să constat lipsa –  pe sistemele unor programatori – a unor  utilitarele mici, inspirate sau portate din lumea Linux. Prin urmare, m-am gândit să încerc să fac un mic ghid despre ce și cum ar trebui instalat.

Plec de la premisa că este și NodeJS instalat iar XAMPP este Stack-ul folosit, dar se poate aplica în orice alt context; scopul este acela de a avea toate utilitarele disponibile în cmd (hint: încearcă cmder)

1. Rapid Environment Editor

Complet opțional, dar foarte la îndemână, util în editarea/adăugarea variabilelor. Sigur, te poți descurca și prin alte metode, dar REE este atât de eficient încât nici nu merită să iau în considerare alte explicații 😀

2. Gow

Este o colecție de utilitare, de genul whereis, wget, curl, grep, tar șamd. În total sunt peste o sută de astfel de programe.

3. GnuWin

Similar cu Gow, dar ultima actualizare a fost în 2009. Eu le am pe amândouă doar pentru că pe ăsta l-am găsit primul și mi-a fost prea lene să le compar 😀

4. Git

Instalat astfel încât Git va fi disponibil direct din cmd. Adică la instalare alegi ultima opțiune:


După ce instalăm tot ce am listat mai sus, deschidem REE și avem grijă ca în PATH să existe directoarele unde au fost instalate utilitarele astea:

Săgețile albastre sunt legate de contul curent din Windows și poate fi aflat prin apăsarea Win+R -> %appData%\npm respectiv %appData\Composer.

Pe lângă utilitarele înșirate mai sus, recomand să adaugi și directoarele bin  de la Apache sau Mysql, respectiv directorul în care se află executabilul php.exe (e.g. c:\xampp\php)

După ce ai adăugat toate variabilele astea în editor, salvezi și dai un restart (unii zic că logout ar fi suficient). Imediat după, dacă deschizi cmd vei avea la dispoziție o mulțime de utilitare, care mai de care mai… utile.

Folosești Composer în tema ta WordPress? Evită erorile la instalare!

De fiecare dată când folosesc Composer într-o temă sau într-un plugin încerc să am grijă de momentul în care trebuie să pornesc de la zero cu respectiva temă (sau plugin).

Cum folderul vendor este ignorat de Git, sunt șanse destul de mari ca tot codul meu să rezulte într-un minunat ecran alb (dacă erorile sunt oprite).

Codul de mai jos încearcă să prevină problema asta, verificând existența autoload.php.

function themeReqirementsWarning_ihdqnrwrbd()
{
	$message = __("Some files are missing from the dependencies list. Please make sure you've ran <code>composer update</code>!", 'felder');
	printf('<div class="error"><p>%s</p></div>', $message);
}

if (!file_exists(get_template_directory() . '/vendor/autoload.php')) {
	add_action('admin_notices', 'themeReqirementsWarning_ihdqnrwrbd');

	add_action('after_switch_theme', function () {
		switch_theme(WP_DEFAULT_THEME, WP_DEFAULT_THEME);
		unset($_GET['activated']);
		add_action('admin_notices', 'themeReqirementsWarning_ihdqnrwrbd');
	});
	return;
}

require_once dirname(__FILE__) . "/vendor/autoload.php";

Conferința Apple WWDC 2017

Mâine începe evenimentul Apple WWDC. Va putea fi urmărit live aici de mâine, începând cu ora 20:00, de pe Safari sau Edge. Desigur, îl vom comenta și pe DevForum!

Ce ar trebui să așteptăm?

Speculații despre noutăți au tot început să apară de câteva luni: actualizări la mac book-uri (CPU mai nou), un nou iPad pro (ramă mai subtire, ecran mai bun) și Siri Speaker – alternativă la Amazon Alexa sau Google Home.

Ce se va anunța la modul sigur: iOS 11 și o nouă versiune de macOS. Despre iOS se speculează că va avea o interfață ușor redesenată și că va renunța la suportul pentru 32bit. Despre macOS se speculează că va trece (sau că va permite trecerea)  la APFS – un file-system nou, deja folosit pe iOS de câteva luni.

Live blog

  • cel mai mare wwdc ever. Se trece de laudele obișnuite pentru că „sunt multe lucruri de discutat”. tvOS, watchOS, macOS și iOS.
  • High Sierra este numele noului macOS
  • Safari este cel mai rapid browser (dar, pe de altă parte, și Edge este cel mai rapid atunci când e prezentat de Microsoft 😀 )
    • Intelligent tracking prevention
    • disabled autoplay pentru filme/audio
  • Mail
    • split screen
    • căutarea folosește spotlight
  • Photos
    • detectarea fețelor e îmbunătățită;
    • îmbunătățiri pentru prelucrarea pozelor
  • APFS este noul system file, folosit implicit de acum înainte. Mai rapid, mai sigur, mai nou.
  • Disponibil în developer beta de azi, public beta spre sfârșitul lunii, lansarea în toamnă;
  • actualizare iMac:
    • panel-uri cu 40% mai luminoase
    • Intel Kaby Lake
    • actualizări la ecrane și la plăcile video;
    • se dublează capacitatea maximă a memoriei: max 32Gb pentru iMac-urile de 21 și maximum 64Gb pentru cele de 27
  • Și mac book-urile primesc actualizare la Kaby Lake

Din toamnă se lansează… iMac Pro ?!

  • ecran 5k;
  • The most powerful mac ever; (deci nu mai vedem mac pro?)
  • 8/10/18 cores Xeon;
  • Plăci video Radeon Vega (max 16gb ram) până la 22 teraflops;
  • Până la 128Gb RAM;
  • 10Gb ethernet;
  • Prețul pleacă de la 5000$, disponibil din decembrie.

iOS

  • Messages
    • mesajele sunt sincronizate între dispozitive;
  • Apple Pay va permite plata între utilizatori.
  • Siri:
    • o exprimare mai clară
    • traducere din engleză în câteva limbi;
  • Camera/Photos
    • un nou format pentru imagini ce promite compresie 2x;
    • Timpi de expunere custom?

  • Maps afișează hărțile din Mall-uri și Aeroporturi
  • App Store
    • Poți face phased deploy la aplicatii, astfel încât, la lansare, să nu îți crape serverele;
    • Redesign pentru app store;
  • API-uri pentru face tracking, face detection etc. Core Machine Learning
  • API pentru augmented reality.

iPad pro

  • Ecran de 10.4 inch la 120Hz, dar e dinamic, în funcție de conținutul afișat;

  • Pencil are un delay de doar 20ms;
  • procesor A10X, 30% mai rapid decât modelul anterior;

  • Un nou dock

Pictograme inteligente în WordPress

S-a întâmplat de câteva ori să am nevoie de afișarea rapidă a unor pictograme (icons) ori într-un post ori într-un comentariu WordPress. Pentru că <img src... este prea mult și potențial inconsistent, am făcut ce face orice om normal: un plugin. Mă rog… plugin 🙂

Parsăm the_content și get_comment_text și căutăm un pattern de forma icon:nume, apoi îl înlocuim cu ce avem nevoie. În cazul de față, svg-uri. Continuă să citești Pictograme inteligente în WordPress

Animații web native în context geo-spațial

Mai jos o definiție clasică:

HÁRTĂ, hărți, s. f. Reprezentare grafică în plan orizontal a suprafeței pământului (totală sau parțială), generalizată și micșorată conform unei anumite scări de proporție și întocmită pe baza unei proiecții cartografice. [dexonline.ro]

, urmată de o descriere personală al aceluiași subiect:

HÁRTĂ, hărți, s. f. Mod grafic de a combina și reprezenta dinamic, pe un substrat geo-spațial, seturi de date, povești și sentimente.

Profitând de creșterea puterii de procesare a masinilor de calcul, hărțile au evoluat dând naștere GIS-ului. Acesta este un nou mediu de prezentare a datelor ce a schimbat complet modul în care vedem și interacționăm cu spațiul înconjurător.

Hărțile au devenit deci la fel de accesibile și versatile ca Internetul, dar au moștenit și multe dintre defectele acestuia. Web-ul este un mediu dinamic, colorat și interactiv iar hărțile noastre pot fi la fel! Doar că fiecare funcționalitate costă (mai ales în mediul mobil): consumul de date este limitat de costuri iar funcționalitățile sunt sugrumate de resursele hardware. Cu alte cuvinte trebuie să fim cumpătați. Continuă să citești Animații web native în context geo-spațial

Ce e nou în Windows Fall Creator Update 2017?

Zilele acestea are loc Microsoft Build 2017, întâlnirea anuală în care își laudă realizările și unde sunt prezentate planurile de viitor. Azi, în cea de-a doua zi, au fost prezentate unele lucruri interesante despre următoarea actualizare a Windows, numită – foarte original! – Fall Creator Update.  Cel mai probabil, această actualizare va veni prin septembrie. Haideți să vedem noutățile promise!

Bash on Windows

Unul dintre feature-urile care a beneficiat de cel mai mare tam-tam este reprezentat de posibilitatea de a rula Ubuntu în Windows. Inițial era destul de limitat, dar a devenit mai potent cu ultima actualizare. Încă nu este o distribuție în adevăratul sens al cuvântului, dar se laudă a fi destul de potentă pentru multe activități. Continuă să citești Ce e nou în Windows Fall Creator Update 2017?