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.
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-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.