Kuandika msimbo unaonyumbulika kwa kutumia SOLID

Kuandika msimbo unaonyumbulika kwa kutumia SOLID

Kutoka kwa mfasiri: iliyochapishwa kwa ajili yako makala na Severin Perez kuhusu kutumia kanuni za SOLID katika upangaji programu. Habari kutoka kwa kifungu itakuwa muhimu kwa Kompyuta na waandaaji wa programu wenye uzoefu.

Ikiwa unajitayarisha, kuna uwezekano mkubwa kwamba umesikia kanuni za MANGO. Huwezesha kitengeneza programu kuandika msimbo safi, ulioundwa vizuri na unaoweza kudumishwa kwa urahisi. Ni muhimu kuzingatia kwamba katika programu kuna mbinu kadhaa za jinsi ya kufanya kazi fulani kwa usahihi. Wataalamu tofauti wana maoni tofauti na uelewa wa "njia sahihi"; yote inategemea uzoefu wa kila mtu. Hata hivyo, mawazo yaliyotangazwa katika SOLID yanakubaliwa na karibu wawakilishi wote wa jumuiya ya IT. Wakawa mahali pa kuanzia kwa kuibuka na ukuzaji wa mazoea mengi mazuri ya usimamizi wa maendeleo.

Hebu tuelewe kanuni za MANGO ni nini na jinsi zinavyotusaidia.

Skillbox inapendekeza: Kozi ya vitendo "Pro ya Msanidi Programu wa Simu".

Tunakukumbusha: kwa wasomaji wote wa "Habr" - punguzo la rubles 10 wakati wa kujiandikisha katika kozi yoyote ya Skillbox kwa kutumia msimbo wa uendelezaji wa "Habr".

MANGO ni nini?

Neno hili ni kifupi, kila herufi ya neno ni mwanzo wa jina la kanuni fulani:

  • SKanuni ya Uwajibikaji isiyo na maana. Moduli inaweza kuwa na sababu moja na moja tu ya mabadiliko.
  • The Okalamu/Kanuni Iliyofungwa (kanuni iliyofunguliwa/iliyofungwa). Madarasa na vipengele vingine vinapaswa kufunguliwa kwa upanuzi, lakini vifungwe kwa marekebisho.
  • β€ŠThe LKanuni ya Ubadilishaji iskov (Kanuni ya badala ya Liskov). Kazi zinazotumia aina ya msingi zinapaswa kuwa na uwezo wa kutumia aina ndogo za aina ya msingi bila kujua.
  • The IKanuni ya Kutenganisha Kiolesuraβ€Š (kanuni ya kutenganisha interface). Vyombo vya programu haipaswi kutegemea njia ambazo hazitumii.
  • The Dependency Inversion Kanuni (kanuni ya ubadilishaji wa utegemezi). Module katika viwango vya juu hazipaswi kutegemea moduli katika viwango vya chini.

Kanuni ya Wajibu Mmoja

β€Š
Kanuni ya Uwajibikaji Mmoja (SRP) inasema kwamba kila darasa au sehemu katika mpango inapaswa kuwajibika kwa sehemu moja tu ya utendaji wa programu hiyo. Zaidi ya hayo, vipengele vya jukumu hili vinapaswa kugawiwa kwa darasa lao wenyewe, badala ya kutawanyika katika madarasa yasiyohusiana. Msanidi na mwinjilisti mkuu wa SRP, Robert S. Martin, anaelezea uwajibikaji kama sababu ya mabadiliko. Hapo awali alipendekeza neno hili kama moja ya vipengele vya kazi yake "Kanuni za Ubunifu Unaoelekezwa na Kitu". Wazo hili linajumuisha muundo mwingi wa muunganisho ambao ulifafanuliwa hapo awali na Tom DeMarco.

Wazo hilo pia lilijumuisha dhana kadhaa zilizoundwa na David Parnas. Yale mawili kuu ni encapsulation na kuficha habari. Parnas alisema kuwa kugawanya mfumo katika moduli tofauti hakupaswi kutegemea uchanganuzi wa michoro ya vizuizi au mtiririko wa utekelezaji. Yoyote ya moduli lazima iwe na suluhisho maalum ambalo hutoa kiwango cha chini cha habari kwa wateja.

Kwa njia, Martin alitoa mfano wa kuvutia na wasimamizi wakuu wa kampuni (COO, CTO, CFO), ambao kila mmoja hutumia programu maalum ya biashara kwa madhumuni tofauti. Matokeo yake, yeyote kati yao anaweza kutekeleza mabadiliko katika programu bila kuathiri maslahi ya wasimamizi wengine.

Kitu cha kimungu

Kama kawaida, njia bora ya kujifunza SRP ni kuiona katika vitendo. Hebu tuangalie sehemu ya programu ambayo HAIFAI Kanuni ya Uwajibikaji Mmoja. Huu ni msimbo wa Ruby ambao unaelezea tabia na sifa za kituo cha anga.

Kagua mfano na ujaribu kuamua yafuatayo:
Majukumu ya vitu hivyo ambavyo vimetangazwa katika darasa la SpaceStation.
Wale ambao wanaweza kuwa na nia ya uendeshaji wa kituo cha nafasi.

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

Kwa kweli, kituo chetu cha anga hakifanyi kazi vizuri (sidhani nitapigiwa simu na NASA hivi karibuni), lakini kuna jambo la kuchanganua hapa.

Kwa hivyo, darasa la SpaceStation lina majukumu kadhaa tofauti (au kazi). Wote wanaweza kugawanywa katika aina:

  • sensorer;
  • vifaa (vya matumizi);
  • mafuta;
  • vichapuzi.

Ingawa hakuna mfanyakazi yeyote wa kituo aliyepangiwa darasa, tunaweza kufikiria kwa urahisi ni nani anayewajibika kwa nini. Uwezekano mkubwa zaidi, mwanasayansi anadhibiti sensorer, mtaalamu wa vifaa anajibika kwa kusambaza rasilimali, mhandisi anajibika kwa usambazaji wa mafuta, na majaribio ya udhibiti wa nyongeza.

Je, tunaweza kusema kuwa mpango huu hauambatani na SRP? Ndiyo, hakika. Lakini darasa la SpaceStation ni "kitu cha mungu" cha kawaida ambacho kinajua kila kitu na hufanya kila kitu. Huu ni muundo mkuu wa kupingana katika upangaji unaolenga kitu. Kwa anayeanza, vitu kama hivyo ni ngumu sana kutunza. Hadi sasa mpango huo ni rahisi sana, ndiyo, lakini fikiria nini kitatokea ikiwa tunaongeza vipengele vipya. Labda kituo chetu cha anga kitahitaji kituo cha matibabu au chumba cha mkutano. Na kadiri utendakazi unavyozidi kuwapo, ndivyo SpaceStation itaongezeka zaidi. Kweli, kwa kuwa kituo hiki kitaunganishwa na wengine, kuhudumia tata nzima itakuwa ngumu zaidi. Matokeo yake, tunaweza kuharibu uendeshaji wa, kwa mfano, accelerators. Ikiwa mtafiti ataomba mabadiliko kwa vitambuzi, hii inaweza kuathiri vyema mifumo ya mawasiliano ya kituo.

Kukiuka kanuni ya SRP kunaweza kutoa ushindi wa busara wa muda mfupi, lakini mwishowe "tutapoteza vita", na itakuwa ngumu sana kudumisha monster kama huyo katika siku zijazo. Ni bora kugawanya programu katika sehemu tofauti za kanuni, ambayo kila mmoja ni wajibu wa kufanya operesheni maalum. Kwa kuelewa hili, wacha tubadilishe darasa la SpaceStation.

Wacha tugawanye uwajibikaji

Hapo juu tulifafanua aina nne za shughuli ambazo zinadhibitiwa na darasa la SpaceStation. Tutazikumbuka tunapozirekebisha. Nambari iliyosasishwa inalingana vyema na 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

Kuna mabadiliko mengi, mpango hakika unaonekana bora sasa. Sasa darasa letu la SpaceStation limekuwa zaidi ya kontena ambalo shughuli huanzishwa kwa sehemu tegemezi, ikiwa ni pamoja na seti ya vitambuzi, mfumo wa ugavi unaotumika, tanki la mafuta na viboreshaji.

Kwa yoyote ya vigezo sasa kuna darasa sambamba: Sensorer; SupplyHold; Tangi la Mafuta; Wasukuma.

Kuna mabadiliko kadhaa muhimu katika toleo hili la msimbo. Jambo ni kwamba kazi za mtu binafsi hazijajumuishwa tu katika madarasa yao wenyewe, zimepangwa kwa njia ambayo inaweza kutabirika na thabiti. Tunaweka vipengele vilivyo na utendakazi sawa ili kufuata kanuni ya ushikamani. Sasa, ikiwa tunahitaji kubadilisha jinsi mfumo unavyofanya kazi, kutoka kwa muundo wa hashi hadi safu, tumia tu darasa la SupplyHold; sio lazima tuguse moduli zingine. Kwa njia hii, ikiwa afisa wa usafirishaji atabadilisha kitu katika sehemu yake, kituo kingine kitabaki sawa. Katika kesi hii, darasa la SpaceStation hata hawatafahamu mabadiliko.

Maafisa wetu wanaofanya kazi kwenye kituo cha anga za juu huenda wanafurahia mabadiliko kwa sababu wanaweza kuomba yale wanayohitaji. Kumbuka kuwa nambari ya kuthibitisha ina mbinu kama vile report_supplies na report_fuel zilizo katika madarasa ya SupplyHold na FuelTank. Je! nini kingetokea ikiwa Dunia ingeomba kubadili jinsi inavyoripoti? Madarasa yote mawili, SupplyHold na FuelTank, yatahitaji kubadilishwa. Je, ikiwa unahitaji kubadilisha jinsi mafuta na vifaa vya matumizi vinavyotolewa? Labda itabidi ubadilishe madarasa yote sawa tena. Na hii tayari ni ukiukaji wa kanuni ya SRP. Hebu kurekebisha hili.

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.

Katika toleo hili la hivi punde la programu, majukumu yamegawanywa katika madarasa mawili mapya, FuelReporter na SupplyReporter. Wote ni watoto wa darasa la Mwandishi. Kwa kuongezea, tuliongeza vijiti vya mfano kwa darasa la SpaceStation ili darasa ndogo linalohitajika liweze kuanzishwa ikiwa ni lazima. Sasa, ikiwa Dunia itaamua kubadilisha kitu kingine, basi tutafanya mabadiliko kwa madaraja, na sio kwa darasa kuu.

Bila shaka, baadhi ya madarasa yetu bado yanategemeana. Kwa hivyo, kitu cha SupplyReporter kinategemea SupplyHold, na FuelReporter inategemea FuelTank. Bila shaka, nyongeza lazima ziunganishwe na tank ya mafuta. Lakini hapa kila kitu tayari kinaonekana kuwa na mantiki, na kufanya mabadiliko haitakuwa vigumu sana - kuhariri msimbo wa kitu kimoja hautaathiri sana mwingine.

Kwa hivyo, tumeunda msimbo wa msimu ambapo majukumu ya kila kitu / madarasa yamefafanuliwa kwa usahihi. Kufanya kazi na nambari kama hiyo sio shida, kuitunza itakuwa kazi rahisi. Tumebadilisha "kitu cha Mungu" kizima kuwa SRP.

Skillbox inapendekeza:

Chanzo: mapenzi.com

Kuongeza maoni