SRP (Single Responsibility Principle) — den första SOLID-principen som anger: varje klass eller modul ska ha exakt en anledning att ändras. Denna princip formulerades av Robert Martin i boken Clean Architecture (2017) och blev grunden för modulär design. Enligt denna bok minskar tillämpning av SRP direkt kopplingen mellan komponenter och eliminerar kaskadförändringar vid förbättring av funktionalitet.
Huvudpunkter
SRP (Single Responsibility Principle) — principen om enda ansvar som säger: varje klass eller modul ska ha exakt en anledning att ändras. Detta betyder inte att en klass ska utföra exakt en operation. Det handlar om en grupp relaterade handlingar som förenas av ett ansvar inför en aktör.
Robert Martin omformulerade SRP i termer av aktörer: en klass bör endast ändras på begäran av en intressent eller en grupp människor. Om två olika aktörer begär ändring av samma klass — är ansvaret felaktigt fördelat.
Exempelvis klassen Employee, som samtidigt beräknar lön (begäran från redovisning) och genererar rapport (begäran från ledning), bryter mot SRP. Ändring av beräkningsregler kan påverka rapportgenereringen och vice versa.
En modul ska ha en och endast en anledning att ändras. Anledningen till förändring bestäms av aktören — den person eller det system som initierar kravet. Om krav från olika aktörer leder till ändring av samma modul — bryter modulen mot SRP.
Konceptet med aktör gör SRP till ett praktiskt verktyg för arkitektonisk analys, inte en abstrakt rekommendation. Vid design av ett system räcker det att fråga: “Vem kommer att begära ändring av denna kod?” — om svaret innehåller mer än en intressent, bör ansvaret delas upp.
Enda ansvar realiseras genom gruppering av metoder som ändras av en anledning. Klassen blir en “samlingspunkt” för relaterad logik, inte en “schweizisk armékniv” för alla tillfällen. Detta förenklar förståelsen av koden: utvecklaren ser klassen och förstår omedelbart dess syfte.
SRP:s funktionsmekanism bygger på regeln om en förändringsaxel. Om funktionalitet kan ändras av oberoende anledningar — bör den flyttas till separata klasser. Förbindelserna mellan dessa klasser byggs genom komposition eller delegering.
Brott mot SRP manifesteras i “gud-klasser” (God Objects) som innehåller dussintals metoder som arbetar med olika data. En sådan klass är svår att testa — test av en metod kräver konfigurering av miljön för alla andra. Ändring av ett ansvar kan förstöra ett annat, vilket gör koden spröd.
I praktiken hjälper SRP utvecklare att svara på frågan “var finns denna kod?”. Om varje ansvar är isolerat i sin egen klass tar det sekunder att hitta den nödvändiga filen. I ett Android-projekt med MVVM-arkitektur innebär detta att UserViewModel endast ansvarar för användarskärmens tillstånd och UserRepository för att hämta data. En utvecklare som söker efter cache-logik går till UserCacheRepository, inte till ViewModel. En sådan organisation av kod accelererar introduktionen av nya teammedlemmar och minskar antalet fel vid refaktorering.
Mobil utveckling ställer särskilda krav på kodens modularitet. Android Fragment eller iOS ViewController blir ofta “attraktionspunkter” för logik: hantering av klick, API-anrop, parsning av svar, uppdatering av UI — allt i en klass. SRP kräver separation av dessa ansvar.
I Android-arkitektur är SRP inbyggt i Googles rekommendationer för Jetpack: ViewModel ansvarar för skärmstatus, Repository för data, UseCase för affärslogik. Varje komponent har en anledning att ändras. I iOS-utveckling följer mönstret MVVM och Coordinator samma logik.
Efterlevnad av SRP i mobila projekt ger mätbara fördelar: minskning av klassstorlek med 40-60%, kortare code-review-tid och minskning av regressionsfel vid tillägg av ny funktionalitet. Isolerade moduler är lättare att täcka med enhetstester och återanvända i andra skärmar.
Enhetstester av klasser som följer SRP kräver färre mock-objekt och konfigurationer. Om en klass har ett ansvar är dess beroenden begränsade. Testet kontrollerar ett beteende, inte en kombination av flera orelaterade scenarier.
Enligt rapporten från Google Testing Blog (2023) visar klasser med enda ansvar 35% högre testtäckning jämfört med aggregerande klasser. Utvecklare skriver mer villigt tester för små, begripliga moduler.
Betrakta en typisk Android-klass som bryter mot SRP — den laddar data, parsar svaret och uppdaterar UI. Efter refaktorering är varje ansvar isolerat i en separat komponent.
// SRP-brott: en klass gör allt
class BadUserProfileActivity {
fun loadUser(userId: Int) {
// HTTP-förfrågan
// JSON-parsning
// UI-uppdatering
// Spara i databas
}
}
// Efter SRP-tillämpning
class UserRepository {
fun getUser(userId: Int): User
}
class UserViewModel {
private val repo: UserRepository
fun loadUser(userId: Int) { }
}
class UserProfileFragment {
fun render(user: User) { }
}
Liknande exempel på iOS Swift med separation av nätverkslager och presentationslager:
// SRP-brott: ViewController hanterar data och UI
class BadProfileViewController: UIViewController {
func viewDidLoad() {
// URLSession-förfrågan
// JSON-avkodning
// Etikettuppdatering
}
}
// Efter SRP-tillämpning
protocol UserServiceProtocol {
func fetchUser(id: Int) async throws -> User
}
class ProfileViewModel {
private let service: UserServiceProtocol
func loadProfile(id: Int) { }
}
class ProfileViewController: UIViewController {
func display(user: User) { }
}
SRP-refaktorering komplicerar inte arkitekturen — den omfördelar ansvar. Mängden kod kan till och med minska genom eliminering av dubbletter. Varje ny klass har ett tydligt syfte och kan utvecklas oberoende.
Komposition hjälper till att följa SRP där arv skapar onödiga kopplingar. Istället för en superklass med dussintals metoder får underklassen en uppsättning specialiserade objekt via konstruktorn. Varje objekt ansvarar för sin egen funktionalitet.
I Android-utveckling tillåter mönstret Decorator att lägga till ansvar utan att ändra den ursprungliga klassen. I iOS separerar Middleware-kedjan i nätverkslagret loggning, cachning och autentisering i separata moduler.
Det vanligaste brottet — “God Class”: en klass som hanterar databas, skickar notifikationer, genererar rapporter och bearbetar användarinmatning. En sådan klass blir en flaskhals i projektet: varje ändring kräver fullständig regressionstestning.
Inom mobil utveckling orsakas SRP-brott av blandning av affärslogik och UI-logik i Activity, Fragment eller ViewController. När onClickListener-metoden samtidigt validerar data, anropar API och uppdaterar knappars synlighet — är detta ett direkt brott mot principen om enda ansvar.
Konsekvenser av SRP-brott inkluderar: svårighet med parallell utveckling (konflikter i en fil), försvårad enhetstestning, hög kostnad för ändringar och minskad kodläsbarhet. Projekt med systematiskt SRP-brott kräver 2-3 gånger mer tid för att lägga till ny funktionalitet.
SRP-brott kan fastställas genom indirekta tecken: en klass innehåller mer än 200 rader, importerar moduler från olika lager av applikationen (UI + network + database), har mer än 5 publika metoder med olika teman. Kohesionsmetrik — en statistisk indikator: låg kohesion av metoder inom en klass indikerar SRP-brott.
För att upptäcka SRP-brott är det användbart att använda statiska analysverktyg: för Android — Detekt med regeln TooManyFunctions, för iOS — SwiftLint med regeln file_length. Dessa verktyg markerar klasser som överskrider tröskelvärden för storlek och komplexitet.
Refaktorering av klasser som bryter mot SRP utförs via Extract Class eller Extract Delegate: en grupp relaterade metoder flyttas till en separat klass och den ursprungliga klassen delegerar anrop till dem. Gradvis tillämpning av sådana refaktoreringar omvandlar “God Class” till en uppsättning löst kopplade moduler, var och en med ett ansvar. Ett sådant tillvägagångssätt möjliggör förbättring av arkitekturen utan att stoppa utvecklingen — refaktorering utförs iterativt, en modul i taget.
Vanliga frågor
Nej. SRP handlar inte om antalet metoder, utan om antalet anledningar till förändring. En klass kan ha tio metoder om de alla tjänar ett ansvar inför en aktör. En metod — är den andra ytterligheten som leder till överdriven fragmentering av kod.
Det är samma princip. Single Responsibility Principle översätts både som “enda ansvar” och “enda skyldighet”. Termen “ansvar” återspeglar essensen mer exakt: det handlar om ansvar inför en aktör, inte om en teknisk funktion.
Repository — det direkta resultatet av att tillämpa SRP på datalagret. Istället för att sprida dataåtkomstlogik över ViewModel eller UseCase, tar Repository på sig ett enda ansvar: att tillhandahålla data med abstraktion av källan. Detta är en klassisk implementering av SRP i mobil arkitektur.
Ja, SRP förbjuder inte beroenden. En klass med ett ansvar kan delegera en del av arbetet till andra klasser genom komposition. Viktigt är att dessa delegerade uppgifter är en del av samma ansvar, inte en oberoende anledning till förändring.
Ställ frågan: “Vilka aktörer kan begära ändring av denna klass?” Om svaret innehåller mer än en aktör — är SRP brutet. Dessutom: försök beskriva klassens syfte i en mening utan konjunktionen “och”. Om det inte går — gör klassen för mycket.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också