Kitba ta 'kodiċi flessibbli bl-użu SOLID

Kitba ta 'kodiċi flessibbli bl-użu SOLID

Mit-traduttur: ippubblikat għalik artiklu ta’ Severin Perez dwar l-użu tal-prinċipji SOLID fl-ipprogrammar. L-informazzjoni mill-artiklu se tkun utli kemm għal dawk li jibdew kif ukoll għal programmaturi b'esperjenza.

Jekk int fl-iżvilupp, x'aktarx li smajt bil-prinċipji SOLID. Jippermettu lill-programmatur jikteb kodiċi nadif, strutturat tajjeb u li jista' jinżamm faċilment. Ta 'min jinnota li fl-ipprogrammar hemm diversi approċċi dwar kif twettaq b'mod korrett xogħol partikolari. Speċjalisti differenti għandhom ideat u fehim differenti tat-"triq it-tajba"; kollox jiddependi fuq l-esperjenza ta 'kull persuna. Madankollu, l-ideat ipproklamati f'SOLID huma aċċettati minn kważi r-rappreżentanti kollha tal-komunità tal-IT. Huma saru l-punt tat-tluq għall-emerġenza u l-iżvilupp ta 'ħafna prattiki tajbin ta' ġestjoni tal-iżvilupp.

Ejja nifhmu x'inhuma l-prinċipji SOLID u kif jgħinuna.

Skillbox jirrakkomanda: Kors prattiku "Mobile Developer PRO".

Infakkrukom: għall-qarrejja kollha ta '"Habr" - skont ta' 10 rublu meta tirreġistra fi kwalunkwe kors ta 'Skillbox billi tuża l-kodiċi promozzjonali "Habr".

X'inhu SOLID?

Dan it-terminu huwa abbrevjazzjoni, kull ittra tat-terminu hija l-bidu tal-isem ta 'prinċipju speċifiku:

  • SPrinċipju ta' Responsabbiltà. Modulu jista' jkollu raġuni waħda u waħda biss għall-bidla.
  • il Opinna/Prinċipju Magħluq (prinċipju miftuħ/magħluq). Klassijiet u elementi oħra għandhom ikunu miftuħa għall-estensjoni, iżda magħluqa għall-modifika.
  •  il Liskov Prinċipju ta' Sostituzzjoni (Prinċipju ta' sostituzzjoni ta' Liskov). Funzjonijiet li jużaw tip bażi għandhom ikunu jistgħu jużaw sottotipi tat-tip bażi mingħajr ma jkunu jafu.
  • il IPrinċipju tas-Segregazzjoni tal-Interface  (prinċipju tas-separazzjoni tal-interface). L-entitajiet tas-softwer m'għandhomx jiddependu fuq metodi li ma jużawx.
  • il DPrinċipju ta' Inverżjoni ta' dipendenza (prinċipju ta' inverżjoni tad-dipendenza). Moduli f'livelli ogħla m'għandhomx jiddependu fuq moduli f'livelli aktar baxxi.

Prinċipju ta' Responsabbiltà Uniku


Il-Prinċipju ta' Responsabbiltà Unika (SRP) jgħid li kull klassi jew modulu fi programm għandu jkun responsabbli għal parti waħda biss tal-funzjonalità ta' dak il-programm. Barra minn hekk, elementi ta' din ir-responsabbiltà għandhom jiġu assenjati lill-klassi tagħhom stess, aktar milli mxerrda fi klassijiet mhux relatati. L-iżviluppatur u l-evanġelista ewlieni tal-SRP, Robert S. Martin, jiddeskrivi r-responsabbiltà bħala r-raġuni għall-bidla. Oriġinarjament ippropona dan it-terminu bħala wieħed mill-elementi tax-xogħol tiegħu "Prinċipji tad-Disinn Orjentat lejn l-Oġġetti". Il-kunċett jinkorpora ħafna mill-mudell tal-konnettività li qabel kien definit minn Tom DeMarco.

Il-kunċett kien jinkludi wkoll diversi kunċetti fformulati minn David Parnas. Iż-żewġ ewlenin huma l-inkapsulament u l-ħabi tal-informazzjoni. Parnas argumenta li d-diviżjoni ta 'sistema f'moduli separati m'għandhiex tkun ibbażata fuq analiżi ta' dijagrammi ta 'blokki jew flussi ta' eżekuzzjoni. Kull wieħed mill-moduli għandu jkun fih soluzzjoni speċifika li tipprovdi minimu ta 'informazzjoni lill-klijenti.

Mill-mod, Martin ta eżempju interessanti ma 'maniġers anzjani ta' kumpanija (COO, CTO, CFO), li kull wieħed minnhom juża softwer kummerċjali speċifiku għal skopijiet differenti. Bħala riżultat, kwalunkwe wieħed minnhom jista 'jimplimenta bidliet fis-softwer mingħajr ma jaffettwa l-interessi ta' maniġers oħra.

Oġġett divin

Bħal dejjem, l-aħjar mod biex titgħallem l-SRP huwa li taraha fl-azzjoni. Ejja nħarsu lejn taqsima tal-programm li MA ssegwix il-Prinċipju ta' Responsabbiltà Unika. Dan huwa kodiċi Ruby li jiddeskrivi l-imġieba u l-attributi tal-istazzjon spazjali.

Irrevedi l-eżempju u ipprova tiddetermina dan li ġej:
Responsabbiltajiet ta' dawk l-oġġetti li huma ddikjarati fil-klassi SpaceStation.
Dawk li jistgħu jkunu interessati fl-operat tal-istazzjon spazjali.

class SpaceStation
  def initialize
    @supplies = {}
    @fuel = 0
  end
 
  def run_sensors
    puts "----- Sensor Action -----"
    puts "Running sensors!"
  end
 
  def load_supplies(type, quantity)
    puts "----- Supply Action -----"
    puts "Loading #{quantity} units of #{type} in the supply hold."
    
    if @supplies[type]
      @supplies[type] += quantity
    else
      @supplies[type] = quantity
    end
  end
 
  def use_supplies(type, quantity)
    puts "----- Supply Action -----"
    if @supplies[type] != nil && @supplies[type] > quantity
      puts "Using #{quantity} of #{type} from the supply hold."
      @supplies[type] -= quantity
    else
      puts "Supply Error: Insufficient #{type} in the supply hold."
    end
  end
 
  def report_supplies
    puts "----- Supply Report -----"
    if @supplies.keys.length > 0
      @supplies.each do |type, quantity|
        puts "#{type} avalilable: #{quantity} units"
      end
    else
      puts "Supply hold is empty."
    end
  end
 
  def load_fuel(quantity)
    puts "----- Fuel Action -----"
    puts "Loading #{quantity} units of fuel in the tank."
    @fuel += quantity
  end
 
  def report_fuel
    puts "----- Fuel Report -----"
    puts "#{@fuel} units of fuel available."
  end
 
  def activate_thrusters
    puts "----- Thruster Action -----"
    if @fuel >= 10
      puts "Thrusting action successful."
      @fuel -= 10
    else
      puts "Thruster Error: Insufficient fuel available."
    end
  end
end

Fil-fatt, l-istazzjon spazjali tagħna ma jiffunzjonax (ma naħsibx li ser inkun nieqes sejħa min-NASA dalwaqt), iżda hemm xi ħaġa x'tanalizza hawn.

Għalhekk, il-klassi SpaceStation għandha diversi responsabbiltajiet (jew kompiti) differenti. Kollha kemm huma jistgħu jinqasmu f'tipi:

  • Sensuri;
  • provvisti (konsumabbli);
  • karburant;
  • aċċeleraturi.

Anke jekk ħadd mill-impjegati tal-istazzjon ma huwa assenjat klassi, nistgħu faċilment nimmaġinaw min hu responsabbli għal xiex. X'aktarx, ix-xjenzat jikkontrolla s-sensuri, il-loġistika huwa responsabbli għall-forniment tar-riżorsi, l-inġinier huwa responsabbli għall-provvisti tal-fjuwil, u l-pilota jikkontrolla l-boosters.

Nistgħu ngħidu li dan il-programm mhuwiex konformi mal-SRP? Iva, żgur. Iżda l-klassi SpaceStation hija "oġġett alla" tipiku li jaf kollox u jagħmel kollox. Dan huwa anti-mudell ewlieni fl-ipprogrammar orjentat lejn l-oġġetti. Għal Bidu, oġġetti bħal dawn huma estremament diffiċli biex jinżammu. S'issa l-programm huwa sempliċi ħafna, iva, imma immaġina x'se jiġri jekk inżidu karatteristiċi ġodda. Forsi l-istazzjon spazjali tagħna se jkollu bżonn stazzjon mediku jew kamra tal-laqgħat. U aktar ma jkun hemm funzjonijiet, aktar SpaceStation se tikber. Ukoll, peress li din il-faċilità se tkun konnessa ma 'oħrajn, is-servizz tal-kumpless kollu se jsir saħansitra aktar diffiċli. Bħala riżultat, nistgħu nfixklu l-operat ta ', pereżempju, aċċeleraturi. Jekk riċerkatur jitlob bidliet fis-sensuri, dan jista 'jaffettwa tajjeb ħafna s-sistemi ta' komunikazzjoni tal-istazzjon.

Il-ksur tal-prinċipju SRP jista 'jagħti rebħa tattika għal żmien qasir, iżda fl-aħħar se "nitilfu l-gwerra", u se jsir diffiċli ħafna li nżommu tali mostru fil-futur. L-aħjar huwa li taqsam il-programm f'taqsimiet separati ta 'kodiċi, li kull waħda minnhom hija responsabbli biex twettaq operazzjoni speċifika. Nifhmu dan, ejja nibdlu l-klassi SpaceStation.

Ejja nqassmu r-responsabbiltà

Hawn fuq iddefinijna erba 'tipi ta' operazzjonijiet li huma kkontrollati mill-klassi SpaceStation. Se nżommuhom f'moħħna meta nirrefactoring. Il-kodiċi aġġornat jaqbel aħjar mal-SRP.

class SpaceStation
  attr_reader :sensors, :supply_hold, :fuel_tank, :thrusters
 
  def initialize
    @supply_hold = SupplyHold.new
    @sensors = Sensors.new
    @fuel_tank = FuelTank.new
    @thrusters = Thrusters.new(@fuel_tank)
  end
end
 
class Sensors
  def run_sensors
    puts "----- Sensor Action -----"
    puts "Running sensors!"
  end
end
 
class SupplyHold
  attr_accessor :supplies
 
  def initialize
    @supplies = {}
  end
 
  def load_supplies(type, quantity)
    puts "----- Supply Action -----"
    puts "Loading #{quantity} units of #{type} in the supply hold."
    
    if @supplies[type]
      @supplies[type] += quantity
    else
      @supplies[type] = quantity
    end
  end
 
  def use_supplies(type, quantity)
    puts "----- Supply Action -----"
    if @supplies[type] != nil && @supplies[type] > quantity
      puts "Using #{quantity} of #{type} from the supply hold."
      @supplies[type] -= quantity
    else
      puts "Supply Error: Insufficient #{type} in the supply hold."
    end
  end
 
  def report_supplies
    puts "----- Supply Report -----"
    if @supplies.keys.length > 0
      @supplies.each do |type, quantity|
        puts "#{type} avalilable: #{quantity} units"
      end
    else
      puts "Supply hold is empty."
    end
  end
end
 
class FuelTank
  attr_accessor :fuel
 
  def initialize
    @fuel = 0
  end
 
  def get_fuel_levels
    @fuel
  end
 
  def load_fuel(quantity)
    puts "----- Fuel Action -----"
    puts "Loading #{quantity} units of fuel in the tank."
    @fuel += quantity
  end
 
  def use_fuel(quantity)
    puts "----- Fuel Action -----"
    puts "Using #{quantity} units of fuel from the tank."
    @fuel -= quantity
  end
 
  def report_fuel
    puts "----- Fuel Report -----"
    puts "#{@fuel} units of fuel available."
  end
end
 
class Thrusters
  def initialize(fuel_tank)
    @linked_fuel_tank = fuel_tank
  end
 
  def activate_thrusters
    puts "----- Thruster Action -----"
    if @linked_fuel_tank.get_fuel_levels >= 10
      puts "Thrusting action successful."
      @linked_fuel_tank.use_fuel(10)
    else
      puts "Thruster Error: Insufficient fuel available."
    end
  end
end

Hemm ħafna bidliet, il-programm żgur jidher aħjar issa. Issa l-klassi SpaceStation tagħna saret aktar kontenitur li fih jinbdew l-operazzjonijiet għal partijiet dipendenti, inkluż sett ta 'sensors, sistema ta' provvista konsumabbli, tank tal-fjuwil, u boosters.

Għal kwalunkwe waħda mill-varjabbli issa hemm klassi korrispondenti: Sensers; SupplyHold; FuelTank; Thrusters.

Hemm diversi bidliet importanti f'din il-verżjoni tal-kodiċi. Il-punt huwa li l-funzjonijiet individwali mhumiex biss inkapsulati fil-klassijiet tagħhom stess, huma organizzati b'tali mod li jsiru prevedibbli u konsistenti. Niggruppaw elementi b'funzjonalità simili biex isegwu l-prinċipju ta 'koerenza. Issa, jekk irridu nbiddlu l-mod kif taħdem is-sistema, nimxu minn struttura hash għal firxa, uża biss il-klassi SupplyHold; m'għandniex għalfejn imissu moduli oħra. B'dan il-mod, jekk l-uffiċjal tal-loġistika jibdel xi ħaġa fit-taqsima tiegħu, il-bqija tal-istazzjon jibqa' intatt. F'dan il-każ, il-klassi SpaceStation lanqas biss se tkun konxja tal-bidliet.

L-uffiċjali tagħna li jaħdmu fuq l-istazzjon spazjali probabbilment huma kuntenti bil-bidliet għax jistgħu jitolbu dawk li għandhom bżonn. Innota li l-kodiċi għandu metodi bħal report_supplies u report_fuel li jinsabu fil-klassijiet SupplyHold u FuelTank. X'jiġri jekk id-Dinja titlob li tibdel il-mod kif tirrapporta? Iż-żewġ klassijiet, SupplyHold u FuelTank, iridu jinbidlu. X'jiġri jekk ikollok bżonn tibdel il-mod kif jitwasslu l-fjuwil u l-konsumabbli? Probabbilment ikollok tibdel l-istess klassijiet kollha mill-ġdid. U dan diġà huwa ksur tal-prinċipju SRP. Ejja nirranġaw dan.

class SpaceStation
  attr_reader :sensors, :supply_hold, :supply_reporter,
              :fuel_tank, :fuel_reporter, :thrusters
 
  def initialize
    @sensors = Sensors.new
    @supply_hold = SupplyHold.new
    @supply_reporter = SupplyReporter.new(@supply_hold)
    @fuel_tank = FuelTank.new
    @fuel_reporter = FuelReporter.new(@fuel_tank)
    @thrusters = Thrusters.new(@fuel_tank)
  end
end
 
class Sensors
  def run_sensors
    puts "----- Sensor Action -----"
    puts "Running sensors!"
  end
end
 
class SupplyHold
  attr_accessor :supplies
  attr_reader :reporter
 
  def initialize
    @supplies = {}
  end
 
  def get_supplies
    @supplies
  end
 
  def load_supplies(type, quantity)
    puts "----- Supply Action -----"
    puts "Loading #{quantity} units of #{type} in the supply hold."
    
    if @supplies[type]
      @supplies[type] += quantity
    else
      @supplies[type] = quantity
    end
  end
 
  def use_supplies(type, quantity)
    puts "----- Supply Action -----"
    if @supplies[type] != nil && @supplies[type] > quantity
      puts "Using #{quantity} of #{type} from the supply hold."
      @supplies[type] -= quantity
    else
      puts "Supply Error: Insufficient #{type} in the supply hold."
    end
  end
end
 
class FuelTank
  attr_accessor :fuel
  attr_reader :reporter
 
  def initialize
    @fuel = 0
  end
 
  def get_fuel_levels
    @fuel
  end
 
  def load_fuel(quantity)
    puts "----- Fuel Action -----"
    puts "Loading #{quantity} units of fuel in the tank."
    @fuel += quantity
  end
 
  def use_fuel(quantity)
    puts "----- Fuel Action -----"
    puts "Using #{quantity} units of fuel from the tank."
    @fuel -= quantity
  end
end
 
class Thrusters
  FUEL_PER_THRUST = 10
 
  def initialize(fuel_tank)
    @linked_fuel_tank = fuel_tank
  end
 
  def activate_thrusters
    puts "----- Thruster Action -----"
    
    if @linked_fuel_tank.get_fuel_levels >= FUEL_PER_THRUST
      puts "Thrusting action successful."
      @linked_fuel_tank.use_fuel(FUEL_PER_THRUST)
    else
      puts "Thruster Error: Insufficient fuel available."
    end
  end
end
 
class Reporter
  def initialize(item, type)
    @linked_item = item
    @type = type
  end
 
  def report
    puts "----- #{@type.capitalize} Report -----"
  end
end
 
class FuelReporter < Reporter
  def initialize(item)
    super(item, "fuel")
  end
 
  def report
    super
    puts "#{@linked_item.get_fuel_levels} units of fuel available."
  end
end
 
class SupplyReporter < Reporter
  def initialize(item)
    super(item, "supply")
  end
 
  def report
    super
    if @linked_item.get_supplies.keys.length > 0
      @linked_item.get_supplies.each do |type, quantity|
        puts "#{type} avalilable: #{quantity} units"
      end
    else
      puts "Supply hold is empty."
    end
  end
end
 
iss = SpaceStation.new
 
iss.sensors.run_sensors
  # ----- Sensor Action -----
  # Running sensors!
 
iss.supply_hold.use_supplies("parts", 2)
  # ----- Supply Action -----
  # Supply Error: Insufficient parts in the supply hold.
iss.supply_hold.load_supplies("parts", 10)
  # ----- Supply Action -----
  # Loading 10 units of parts in the supply hold.
iss.supply_hold.use_supplies("parts", 2)
  # ----- Supply Action -----
  # Using 2 of parts from the supply hold.
iss.supply_reporter.report
  # ----- Supply Report -----
  # parts avalilable: 8 units
 
iss.thrusters.activate_thrusters
  # ----- Thruster Action -----
  # Thruster Error: Insufficient fuel available.
iss.fuel_tank.load_fuel(100)
  # ----- Fuel Action -----
  # Loading 100 units of fuel in the tank.
iss.thrusters.activate_thrusters
  # ----- Thruster Action -----
  # Thrusting action successful.
  # ----- Fuel Action -----
  # Using 10 units of fuel from the tank.
iss.fuel_reporter.report
  # ----- Fuel Report -----
# 90 units of fuel available.

F'din l-aħħar verżjoni tal-programm, ir-responsabbiltajiet ġew maqsuma f'żewġ klassijiet ġodda, FuelReporter u SupplyReporter. It-tnejn huma tfal tal-klassi Reporter. Barra minn hekk, żidna varjabbli ta’ istanza mal-klassi SpaceStation sabiex is-subklassi mixtieqa tkun tista’ tiġi inizjalizzata jekk meħtieġ. Issa, jekk id-Dinja tiddeċiedi li tibdel xi ħaġa oħra, allura aħna se nagħmlu bidliet fis-subklassijiet, u mhux fil-klassi prinċipali.

Naturalment, xi wħud mill-klassijiet tagħna għadhom jiddependu minn xulxin. Għalhekk, l-oġġett SupplyReporter jiddependi fuq SupplyHold, u FuelReporter jiddependi fuq FuelTank. Naturalment, il-boosters għandhom ikunu konnessi mat-tank tal-karburant. Iżda hawnhekk kollox diġà jidher loġiku, u li tagħmel bidliet mhux se jkun partikolarment diffiċli - l-editjar tal-kodiċi ta 'oġġett wieħed mhux se jaffettwa ħafna ieħor.

Għalhekk, ħloqna kodiċi modulari fejn ir-responsabbiltajiet ta 'kull wieħed mill-oġġetti/klassijiet huma definiti b'mod preċiż. Li taħdem b'tali kodiċi mhix problema, iż-żamma tagħha se tkun biċċa xogħol sempliċi. Aħna kkonvertijna l-"oġġett divin" kollu f'SRP.

Skillbox jirrakkomanda:

Sors: www.habr.com

Żid kumment