Nivîsandina koda maqûl bi karanîna SOLID

Nivîsandina koda maqûl bi karanîna SOLID

Ji wergêr: ji bo we hatiye weşandin gotara Severin Perez derbarê bikaranîna prensîbên SOLID di bernamekirinê de. Agahdariya ji gotarê dê hem ji bo destpêk û hem jî ji bo bernamenûsên bi ezmûn re kêrhatî be.

Heke hûn di pêşveçûnê de ne, we bi îhtîmalek pir prensîbên SOLID bihîstiye. Ew bernamesaz dihêlin ku kodek paqij, birêkûpêk û bi hêsanî were domandin binivîse. Hêjayî gotinê ye ku di bernamekirinê de çend nêzîkatî hene ka meriv çawa karek taybetî bi rast bi cih tîne. Pisporên cihêreng xwedan raman û têgihiştina "rêya rast" cûda ne; ew hemî bi ezmûna her kesê ve girêdayî ye. Lêbelê, ramanên ku di SOLID de têne ragihandin hema hema ji hêla hemî nûnerên civaka IT-ê ve têne pejirandin. Ew bûn xala destpêkê ji bo derketin û pêşkeftina gelek pratîkên rêveberiya pêşkeftinê yên baş.

Ka em fêm bikin ka prensîbên SOLID çi ne û ew çawa alîkariya me dikin.

Skillbox pêşniyar dike: Kursa pratîk "Pêşvebirê Mobîl PRO".

Em bînin bîra xwe: ji bo hemî xwendevanên "Habr" - dema ku hûn beşdarî qursek Skillbox-ê bi karanîna koda danasînê ya "Habr" têne qeyd kirin 10 rubleyan dakêşin.

SOLID çi ye?

Ev term kurteyek e, her tîpa termê destpêka navê prensîbek taybetî ye:

  • Single Prensîba Berpirsiyariyê. Modulek dikare yek û yek sedemek ji bo guhertinê hebe.
  • Ew Opênûs / Prensîba Girtî (prensîba vekirî / girtî). Divê ders û hêmanên din ji bo dirêjkirinê vekirî bin, lê ji bo guhartinê girtî bin.
  •  Ew LPrensîba Cîgirkirina iskov (prensîba cîgirkirina Lîskov). Fonksiyonên ku celebek bingehîn bikar tînin divê bikaribin bêyî ku zanibin bincûreyên celebê bingehîn bikar bînin.
  • Ew IPrensîba Veqetandina Navberê  (prensîba veqetandina navrûyê). Divê saziyên nermalavê bi rêbazên ku ew bikar neynin ve girêdayî nebin.
  • Ew Dependency Prensîba Veguhastina (prensîba veguhertina girêdayîbûnê). Divê modulên di astên bilind de bi modulên di astên jêrîn de venegerin.

Prensîba Berpirsiyariya Yekane


Prensîba Berpirsiyariya Yekane (SRP) diyar dike ku her çîn an modulek di bernameyekê de divê tenê ji yek beşek fonksiyona wê bernameyê berpirsiyar be. Wekî din, hêmanên vê berpirsiyariyê divê ji çîna xwe re bêne destnîşankirin, ne ku li çînên negirêdayî belav bibin. Pêşdebir û mizgînvanê sereke yê SRP, Robert S. Martin, berpirsiyarî wekî sedema guherînê binav dike. Wî di destpêkê de ev term wekî yek ji hêmanên xebata xwe "Prensîbên Sêwirana Objekt-Oriented" pêşniyar kir. Têgeh pir ji şêwaza girêdanê ya ku berê ji hêla Tom DeMarco ve hatî destnîşankirin vedihewîne.

Di nav vê konseptê de gelek têgehên ku ji hêla David Parnas ve hatine formulekirin jî hene. Du yên sereke encapsasyon û veşartina agahdarî ne. Parnas angaşt kir ku dabeşkirina pergalek li modulên cihêreng nabe ku li ser bingeha analîzkirina diagramên blokan an herikên darvekirinê be. Pêdivî ye ku yek ji modulan çareseriyek taybetî hebe ku hindiktirîn agahdarî ji xerîdaran re peyda dike.

Bi awayê, Martin bi rêveberên payebilind ên pargîdaniyek (COO, CTO, CFO) re mînakek balkêş da, ku her yek ji wan nermalava karsaziya taybetî ji bo mebestên cihê bikar tîne. Wekî encamek, yek ji wan dikare di nermalavê de guhertinan pêk bîne bêyî ku bandorê li berjewendiyên rêveberên din bike.

Tişta Xwedayî

Mîna her gav, awayê çêtirîn fêrbûna SRP ev e ku meriv wê di çalakiyê de bibîne. Ka em li beşek bernameyê binêrin ku Prensîba Berpirsiyariya Yekane li pey NE. Ev koda Ruby e ku tevger û taybetmendiyên stasyona fezayê vedibêje.

Mînakê binihêrin û hewl bidin ku tiştên jêrîn diyar bikin:
Berpirsiyarên wan tiştên ku di çîna SpaceStation de têne ragihandin.
Yên ku dikarin bi operasyona îstasyona fezayê re eleqedar bibin.

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

Bi rastî, qereqola meya fezayê bêfonksîyonel e (Ez nafikirim ku ez ê di demek nêzîk de ji NASA re têlefonek bistînim), lê li vir tiştek heye ku meriv analîz bike.

Bi vî rengî, çîna SpaceStation xwedan çend berpirsiyariyên (an peywirên) cihêreng e. Hemî wan dikarin li cûrbecûr bêne dabeş kirin:

  • sensors;
  • malzemeyên (malbatan);
  • malê şewatê;
  • accelerators.

Her çend ji yek ji xebatkarên qereqolê çînek neyê dayîn jî, em dikarin bi hêsanî bifikirin ka kî ji çi berpirsiyar e. Bi îhtimaleke mezin, zanyar senzoran kontrol dike, lojîstîk berpirsiyarê peydakirina çavkaniyan e, endezyar berpirsiyarê dabînkirina sotemeniyê ye, û pîlot pîlotan kontrol dike.

Ma em dikarin bibêjin ku ev bername ne li gorî SRP ye? Erê, bê guman. Lê çîna SpaceStation "tiştek Xwedê" ya tîpîk e ku her tiştî dizane û her tiştî dike. Ev di bernamesaziya objekt-oriented de antî-patternek sereke ye. Ji bo destpêkek, tiştên weha pir dijwar e ku meriv xwe biparêze. Heya nuha bername pir hêsan e, erê, lê bifikirin ka dê çi bibe heke em taybetmendiyên nû lê zêde bikin. Dibe ku qereqola meya fezayê hewceyê qereqolek bijîşkî an jûreyek civînê be. Û çiqas bêtir fonksiyon hene, SpaceStation dê bêtir mezin bibe. Welê, ji ber ku ev tesîs dê bi yên din ve were girêdan, xizmetkirina tevahiya kompleksê dê hîn tevlihevtir bibe. Wekî encamek, em dikarin xebata, wek nimûne, bilezkeran asteng bikin. Ger lêkolînerek guheztina senzoran bixwaze, ev dikare pir baş bandorê li pergalên ragihandinê yên qereqolê bike.

Binpêkirina prensîba SRP dibe ku serkeftinek taktîkî ya demkurt bide, lê di dawiyê de em ê "şer winda bikin", û di pêşerojê de domandina cinawirek wusa dê pir dijwar bibe. Ya çêtirîn e ku meriv bernameyê li beşên cihê yên kodê veqetîne, ku her yek ji wan berpirsiyar e ku karek taybetî pêk bîne. Fêmkirina vê yekê, em çîna SpaceStation biguherînin.

Werin em berpirsiyariyê belav bikin

Li jor me çar celeb operasyonên ku ji hêla çîna SpaceStation ve têne kontrol kirin diyar kirin. Em ê wan di hişê xwe de di dema vesazkirinê de bihêlin. Koda nûvekirî çêtir bi SRP-ê re têkildar e.

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

Gelek guhertin hene, bername bê guman niha çêtir xuya dike. Naha pola meya SpaceStation bêtir bûye konteynerek ku tê de operasyon ji bo beşên girêdayî têne destpêkirin, di nav de komek senzor, pergalek peydakirina vexwarinê, tankek sotemeniyê, û bihêzker.

Ji bo her yek ji guherbaran niha çînek têkildar heye: Sensor; SupplyHold; FuelTank; Thrusters.

Di vê guhertoya kodê de çend guhertinên girîng hene. Mesele ev e ku fonksiyonên ferdî ne tenê di çînên xwe de têne veqetandin, ew bi vî rengî têne organîze kirin ku bibin pêşbînîkirî û hevgirtî. Em hêmanên bi fonksiyonên wekhev kom dikin da ku prensîba hevrêziyê bişopînin. Naha, ger hewce be ku em awayê xebitandina pergalê biguhezînin, ji avahiyek hash berbi rêzek veguhezînin, tenê çîna SupplyHold bikar bînin; ne hewce ye ku em dest bidin modulên din. Bi vî awayî, heke karmendê lojîstîkê di beşa xwe de tiştek biguhezîne, dê qereqola mayî sax bimîne. Di vê rewşê de, çîna SpaceStation dê ji guhertinan jî hay nebe.

Karbidestên me yên ku li stasyona fezayê dixebitin dibe ku ji guhertinan kêfxweş in ji ber ku ew dikarin yên ku hewce dikin bixwazin. Bala xwe bidinê ku kod rêbazên wekî report_supplies û report_fuel di çînên SupplyHold û FuelTank de hene. Ger Erd daxwaz bike ku awayê ragihandina xwe biguhezîne dê çi bibe? Her du çîn, SupplyHold û FuelTank, dê hewce ne ku bêne guhertin. Heke hûn hewce ne ku awayê radestkirina sotemenî û xerckirinê biguhezînin çi? Dibe ku hûn ê neçar bimînin ku dîsa hemî heman dersan biguherînin. Û ev jixwe binpêkirina prensîba SRP ye. Ka em vê rast bikin.

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.

Di vê guhertoya herî dawî ya bernameyê de, berpirsiyarî li du çînên nû, FuelReporter û SupplyReporter hatine dabeş kirin. Ew her du jî zarokên pola Nûçegihanê ne. Digel vê yekê, me guhêrbarên nimûneyê li pola SpaceStation zêde kir da ku ger hewce be jêr-classa xwestî were destpêkirin. Naha, heke Erd biryar bide ku tiştek din biguhezîne, wê hingê em ê guhartinan di bin-classan de bikin, û ne li pola sereke.

Bê guman, hin dersên me hîn jî bi hev ve girêdayî ne. Bi vî rengî, tişta SupplyReporter bi SupplyHold ve girêdayî ye, û FuelReporter bi FuelTank ve girêdayî ye. Bê guman, boosters divê bi tanka sotemeniyê ve girêdayî bin. Lê li vir her tişt jixwe mentiqî xuya dike, û çêkirina guhertinan dê bi taybetî ne dijwar be - guheztina koda yek tişt dê pir bandorek din neke.

Bi vî rengî, me kodek modular çêkiriye ku tê de berpirsiyariyên her yek ji hêmanan / polan bi tam têne destnîşan kirin. Karkirina bi kodek wusa ne pirsgirêkek e, domandina wê dê karek hêsan be. Me tevahiya "tişta xwedayî" veguherandiye SRP.

Skillbox pêşniyar dike:

Source: www.habr.com

Add a comment