One level of abstraction per function - Simplification

Writing code is all about abstractions. Abstraction helps programmers write better, well-designed code.

One of the main principles that every developer needs to follow for reducing code complexity and avoiding code smells is the fundamental principle called “One level of abstraction” or better known as “SLA - Single Level of Abstraction.” It’s a powerful concept, and like all other powerful concepts, it comes with a price. If not used well, it can also cause damage.

The main idea of this principle is to avoid mixing multiple levels of abstraction in one method. As with any other principle, this one is not mandatory for use, but the benefits from following it are enormous. Most importantly, it can help increase the readability and simplicity of the code.

Excellent code readability and simplicity are often the most difficult to achieve, but always striving for them is priceless for the future of programming.

If followed, this principle ensures that functions do one and only one thing, and they do it well.

Even though it sounds simple, in reality, this principle can be hard to understand and follow.

In the examples below, I will explain this principle in detail through practical examples.

“Mixing levels of abstraction within a function is always confusing.” - Robert C. Martin

Simplification

Let’s try to simplify a couple of examples to help break down and understand this principle.

Order processing

In this example, we will explain the principle in the context of order processing, specifically the order shipping feature. Let’s do it:

ℹ️ Note: Code examples in this article were syntax-checked and completed with AI assistance to ensure correctness across multiple programming languages. ℹ️
  • def ship_order
          if @order.get_payment_state === 'paid' && 
              @order.get_shipment_state != 'shipped'
            
            @order.set_as_shipped()
            
            _email_template = 'order_shipped'
            _email_content = @emailTemplateBuilder.build(
                _email_template,
                @order
            ) 
            
            @emailService.notify(
                _email_content,
                Array([@order.get_email()])
            );
            
            _tracking_number = @trackingNumberService.fetch_tracking_number(
                @order
            );
            @order.set_tracking_number(_tracking_number);
            
            @order.save();      
          end
      end
  • public function shipOrder(): void
    {
        if (
            $this->order->getPaymentState() === 'paid' &&
            $this->order->getShipmentState() !== 'shipped'
        ) {
            $this->order->setAsShipped();
    
            $emailTemplate = 'order_shipped';
            $emailContent = $this->emailTemplateBuilder->build(
                $emailTemplate,
                $this->order
            );
    
            $this->emailService->notify(
                $emailContent,
                [$this->order->getEmail()]
            );
    
            $trackingNumber = $this->trackingNumberService->fetchTrackingNumber(
                $this->order
            );
    
            $this->order->setTrackingNumber($trackingNumber);
    
            $this->order->save();
        }
    }
  • func ShipOrder(order Order) {
        if order.GetPaymentState() == "paid" && 
           order.GetShipmentState() != "shipped" {
            order.SetAsShipped()
            
            emailTemplate := "order_shipped"
            emailContent := emailTemplateBuilder.Build(
                emailTemplate,
                order,
            )
            
            emailService.Notify(
                emailContent,
                []string{order.GetEmail()},
            )
            
            trackingNumber := trackingNumberService.FetchTrackingNumber(
                order,
            )
            order.SetTrackingNumber(trackingNumber)
            
            order.Save()
        }
    }
  • def ship_order(self):
        if (self.order.get_payment_state() == 'paid' and
           self.order.get_shipment_state() != 'shipped'):
            self.order.set_as_shipped()
    
            _email_template = 'order_shipped'
            _email_content = self.email_template_builder.build(
                _email_template,
                self.order
            )
    
            self.email_service.notify(
                _email_content,
                [self.order.get_email()]
            )
    
            _tracking_number = self.tracking_number_service.fetch_tracking_number(
                self.order
            )
            self.order.set_tracking_number(_tracking_number)
    
            self.order.save()
  • shipOrder() {
        if (
            this.order.getPaymentState() === 'paid' && 
            this.order.getShipmentState() !== 'shipped'
        ) {           
            this.order.setAsShipped();
            
            const emailTemplate = 'order_shipped';
            const emailContent = this.emailTemplateBuilder.build(
                emailTemplate,
                this.order
            ); 
            this.emailService.notify(
                emailContent,
                [this.order.getEmail()]
            );
            
            const trackingNumber = this.trackingNumberService.fetchTrackingNumber(
                this.order
            );
            this.order.setTrackingNumber(trackingNumber);
            
            this.order.save();    
        }
    }
  • public void shipOrder(Order order) 
    {
        if (
            order.getPaymentState().equals("paid") &&
            !order.getShipmentState().equals("shipped")
        ) {
            order.setAsShipped();
    
            String emailTemplate = "order_shipped";
            String emailContent = emailTemplateBuilder.build(
                emailTemplate,
                order
            );
            emailService.notify(
                emailContent,
                order.getEmail()
            );
    
            String trackingNumber = trackingNumberService.fetchTrackingNumber(
                order
            );
            order.setTrackingNumber(trackingNumber);
    
            order.save();
        }
    }
  • void ShipOrder(Order order) 
    {
        if (
            order.GetPaymentState() == "paid" &&
            order.GetShipmentState() != "shipped"
        ) {
            order.SetAsShipped();
    
            string emailTemplate = "order_shipped";
            string emailContent = emailTemplateBuilder.Build(
                emailTemplate,
                order
            );
            emailService.Notify(
                emailContent,
                order.GetEmail()
            );
    
            string trackingNumber = trackingNumberService.FetchTrackingNumber(
                order
            );
            order.SetTrackingNumber(trackingNumber);
    
            order.Save();
        }
    }
  • fn ship_order(order: &mut Order) {
        if order.get_payment_state() == "paid" && 
           order.get_shipment_state() != "shipped" {
            order.set_as_shipped();
    
            let email_template = "order_shipped";
            let email_content = email_template_builder.build(
                email_template,
                order
            );
    
            email_service.notify(
                &email_content,
                vec![order.get_email()]
            );
    
            let tracking_number = tracking_number_service.fetch_tracking_number(
                order
            );
            order.set_tracking_number(&tracking_number);
    
            order.save();
        }
    }

The code above makes it crystal clear that the method used for shipping orders is doing a lot. In other words, it has multiple levels of abstractions inside method implementation.

The following points represent different levels of abstraction that need to be extracted from the ship order method:

  • The code responsible for checking if the order is ready for shipping.
  • Building the email template needed for shipped order notification.
  • Sending the notification from inside the method.
  • Fetching and attaching the tracking number to the order.

Let’s do the work and apply SLA principle to improve the method.

  • def ship_order
      if (self.order_not_shippable())
          return;
      end
    
      @order.set_as_shipped()
    
      self.notify_customer();
      self.track_order();
    
      @order.save();
    end
    
    def order_not_shippable
      return  (@order.get_payment_state != 'paid' || @order.get_shipment_state == 'shipped')
    end
    
    def notify_customer
      _email_template = 'order_shipped'
      _email_content = @emailTemplateBuilder.build(
          _email_template,
          @order
      )
    
      @emailService.notify(
          _email_content,
          [@order.get_email()]
      )
    end
    
    def track_order
      _tracking_number = @trackingNumberService.fetch_tracking_number(
          @order
      )
    
      @order.set_tracking_number(_tracking_number)
    end
  • public function shipOrder(): void
    {
        if ($this->orderNotShippable()) {
            return;
        }
    
        $this->order->setAsShipped();
    
        $this->notifyCustomer();
        $this->trackOrder();
    
        $this->order->save();
    }
    
    private function orderNotShippable(): bool
    {
        return  $this->order->getPaymentState() !== 'paid' ||
                $this->order->getShipmentState() === 'shipped';
    }
    
    private function notifyCustomer(): void
    {
        $emailTemplate = 'order_shipped';
        $emailContent = $this->emailTemplateBuilder->build(
            $emailTemplate,
            $this->order
        );
    
        $this->emailService->notify(
            $emailContent,
            [$this->order->getEmail()]
        );
    }
    
    private function trackOrder(): void
    {
        $trackingNumber = $this->trackingNumberService->fetchTrackingNumber(
            $this->order
        );
    
        $this->order->setTrackingNumber($trackingNumber);
    }
  • func ShipOrder(order Order) {
        if OrderNotShippable(order) {
            return
        }
    
        order.SetAsShipped()
    
        NotifyCustomer(order)
        TrackOrder(order)
    
        order.Save()
    }
    
    func OrderNotShippable(order Order) bool {
        return order.GetPaymentState() != "paid" ||
               order.GetShipmentState() == "shipped"
    }
    
    func NotifyCustomer(order Order) {
        emailTemplate := "order_shipped"
        emailContent := emailTemplateBuilder.Build(
            emailTemplate,
            order,
        )
    
        emailService.Notify(
            emailContent,
            []string{order.GetEmail()},
        )
    }
    
    func TrackOrder(order Order) {
        trackingNumber := trackingNumberService.FetchTrackingNumber(
            order,
        )
    
        order.SetTrackingNumber(trackingNumber)
    }
  • def ship_order(self):
        if self.order_not_shippable():
            return
    
        self.order.set_as_shipped()
    
        self.notify_customer()
        self.track_order()
    
        self.order.save()
    
    def order_not_shippable(self):
        return (self.order.get_payment_state() != 'paid' or 
                self.order.get_shipment_state() == 'shipped')
    
    def notify_customer(self):
        _email_template = 'order_shipped'
        _email_content = self.email_template_builder.build(
            _email_template,
            self.order
        )
    
        self.email_service.notify(
            _email_content,
            [self.order.get_email()]
        )
    
    def track_order(self):
        _tracking_number = self.tracking_number_service.fetch_tracking_number(
            self.order
        )
        self.order.set_tracking_number(_tracking_number)
  • shipOrder() {
        if (this.orderNotShippable()) {
            return;
        }
    
        this.order.setAsShipped();
    
        this.notifyCustomer();
        this.trackOrder();
    
        this.order.save();
    }
    
    orderNotShippable() {
        return this.order.getPaymentState() !== 'paid' ||
               this.order.getShipmentState() === 'shipped';
    }
    
    notifyCustomer() {
        const emailTemplate = 'order_shipped';
        const emailContent = this.emailTemplateBuilder.build(
            emailTemplate,
            this.order
        );
    
        this.emailService.notify(
            emailContent,
            [this.order.getEmail()]
        );
    }
    
    trackOrder() {
        const trackingNumber = this.trackingNumberService.fetchTrackingNumber(
            this.order
        );
    
        this.order.setTrackingNumber(trackingNumber);
    }
  • public void shipOrder(Order order)
    {
        if (orderNotShippable(order)) {
            return;
        }
    
        order.setAsShipped();
    
        notifyCustomer(order);
        trackOrder(order);
    
        order.save();
    }
    
    private boolean orderNotShippable(Order order)
    {
        return !order.getPaymentState().equals("paid") ||
               order.getShipmentState().equals("shipped");
    }
    
    private void notifyCustomer(Order order)
    {
        String emailTemplate = "order_shipped";
        String emailContent = emailTemplateBuilder.build(
            emailTemplate,
            order
        );
    
        emailService.notify(
            emailContent,
            order.getEmail()
        );
    }
    
    private void trackOrder(Order order)
    {
        String trackingNumber = trackingNumberService.fetchTrackingNumber(
            order
        );
    
        order.setTrackingNumber(trackingNumber);
    }
  • void ShipOrder(Order order)
    {
        if (OrderNotShippable(order)) {
            return;
        }
    
        order.SetAsShipped();
    
        NotifyCustomer(order);
        TrackOrder(order);
    
        order.Save();
    }
    
    bool OrderNotShippable(Order order)
    {
        return order.GetPaymentState() != "paid" ||
               order.GetShipmentState() == "shipped";
    }
    
    void NotifyCustomer(Order order)
    {
        string emailTemplate = "order_shipped";
        string emailContent = emailTemplateBuilder.Build(
            emailTemplate,
            order
        );
        emailService.Notify(
            emailContent,
            order.GetEmail()
        );
    }
    
    void TrackOrder(Order order)
    {
        string trackingNumber = trackingNumberService.FetchTrackingNumber(
            order
        );
    
        order.SetTrackingNumber(trackingNumber);
    }
  • fn ship_order(order: &mut Order) {
        if order_not_shippable(order) {
            return;
        }
    
        order.set_as_shipped();
    
        notify_customer(order);
        track_order(order);
    
        order.save();
    }
    
    fn order_not_shippable(order: &Order) -> bool {
        order.get_payment_state() != "paid" ||
        order.get_shipment_state() == "shipped"
    }
    
    fn notify_customer(order: &Order) {
        let email_template = "order_shipped";
        let email_content = email_template_builder.build(
            email_template,
            order
        );
    
        email_service.notify(
            &email_content,
            vec![order.get_email()]
        );
    }
    
    fn track_order(order: &mut Order) {
        let tracking_number = tracking_number_service.fetch_tracking_number(
            order
        );
    
        order.set_tracking_number(&tracking_number);
    }

It looks so much better!

Every line in the ship method is now at the same level of detail as the other lines. This means they are all on the same level of abstraction.

User Registration

In this example, we will demonstrate the principle using a user registration feature. This will show how to properly separate concerns when creating a new user account.

ℹ️ Note: Code examples in this article were syntax-checked and completed with AI assistance to ensure correctness across multiple programming languages. ℹ️
  • def register_user
      if @user_data.get_email != nil && 
         @user_data.get_password != nil &&
         @user_data.get_password.length >= 8
        
        _hashed_password = @passwordHasher.hash(@user_data.get_password)
        @user.set_password(_hashed_password)
        @user.set_email(@user_data.get_email)
        @user.set_name(@user_data.get_name)
        
        @user.save()
        
        _verification_token = SecureRandom.hex(32)
        @user.set_verification_token(_verification_token)
        @user.save()
        
        _email_template = 'welcome_email'
        _email_content = @emailTemplateBuilder.build(
            _email_template,
            @user
        )
        
        @emailService.send(
            _email_content,
            @user.get_email()
        )
        
        _log_message = "User registered: " + @user.get_email()
        @logger.info(_log_message)
      end
    end
  • public function registerUser(): void
    {
        if (
            $this->userData->getEmail() !== null &&
            $this->userData->getPassword() !== null &&
            strlen($this->userData->getPassword()) >= 8
        ) {
            $hashedPassword = $this->passwordHasher->hash(
                $this->userData->getPassword()
            );
            $this->user->setPassword($hashedPassword);
            $this->user->setEmail($this->userData->getEmail());
            $this->user->setName($this->userData->getName());
            
            $this->user->save();
            
            $verificationToken = bin2hex(random_bytes(16));
            $this->user->setVerificationToken($verificationToken);
            $this->user->save();
            
            $emailTemplate = 'welcome_email';
            $emailContent = $this->emailTemplateBuilder->build(
                $emailTemplate,
                $this->user
            );
            
            $this->emailService->send(
                $emailContent,
                $this->user->getEmail()
            );
            
            $logMessage = "User registered: " . $this->user->getEmail();
            $this->logger->info($logMessage);
        }
    }
  • func RegisterUser(userData UserData) {
        if userData.GetEmail() != "" &&
           userData.GetPassword() != "" &&
           len(userData.GetPassword()) >= 8 {
            hashedPassword := passwordHasher.Hash(
                userData.GetPassword(),
            )
            user.SetPassword(hashedPassword)
            user.SetEmail(userData.GetEmail())
            user.SetName(userData.GetName())
            
            user.Save()
            
            verificationToken := generateRandomToken()
            user.SetVerificationToken(verificationToken)
            user.Save()
            
            emailTemplate := "welcome_email"
            emailContent := emailTemplateBuilder.Build(
                emailTemplate,
                user,
            )
            
            emailService.Send(
                emailContent,
                user.GetEmail(),
            )
            
            logMessage := "User registered: " + user.GetEmail()
            logger.Info(logMessage)
        }
    }
  • def register_user(self):
        if (self.user_data.get_email() is not None and
           self.user_data.get_password() is not None and
           len(self.user_data.get_password()) >= 8):
            hashed_password = self.password_hasher.hash(
                self.user_data.get_password()
            )
            self.user.set_password(hashed_password)
            self.user.set_email(self.user_data.get_email())
            self.user.set_name(self.user_data.get_name())
            
            self.user.save()
            
            verification_token = secrets.token_hex(16)
            self.user.set_verification_token(verification_token)
            self.user.save()
            
            email_template = 'welcome_email'
            email_content = self.email_template_builder.build(
                email_template,
                self.user
            )
            
            self.email_service.send(
                email_content,
                self.user.get_email()
            )
            
            log_message = "User registered: " + self.user.get_email()
            self.logger.info(log_message)
  • registerUser() {
        if (
            this.userData.getEmail() !== null &&
            this.userData.getPassword() !== null &&
            this.userData.getPassword().length >= 8
        ) {
            const hashedPassword = this.passwordHasher.hash(
                this.userData.getPassword()
            );
            this.user.setPassword(hashedPassword);
            this.user.setEmail(this.userData.getEmail());
            this.user.setName(this.userData.getName());
            
            this.user.save();
            
            const verificationToken = crypto.randomBytes(16).toString('hex');
            this.user.setVerificationToken(verificationToken);
            this.user.save();
            
            const emailTemplate = 'welcome_email';
            const emailContent = this.emailTemplateBuilder.build(
                emailTemplate,
                this.user
            );
            
            this.emailService.send(
                emailContent,
                this.user.getEmail()
            );
            
            const logMessage = "User registered: " + this.user.getEmail();
            this.logger.info(logMessage);
        }
    }
  • public void registerUser(UserData userData) 
    {
        if (
            userData.getEmail() != null &&
            userData.getPassword() != null &&
            userData.getPassword().length() >= 8
        ) {
            String hashedPassword = passwordHasher.hash(
                userData.getPassword()
            );
            user.setPassword(hashedPassword);
            user.setEmail(userData.getEmail());
            user.setName(userData.getName());
            
            user.save();
            
            String verificationToken = generateRandomToken();
            user.setVerificationToken(verificationToken);
            user.save();
            
            String emailTemplate = "welcome_email";
            String emailContent = emailTemplateBuilder.build(
                emailTemplate,
                user
            );
            
            emailService.send(
                emailContent,
                user.getEmail()
            );
            
            String logMessage = "User registered: " + user.getEmail();
            logger.info(logMessage);
        }
    }
  • void RegisterUser(UserData userData) 
    {
        if (
            userData.GetEmail() != null &&
            userData.GetPassword() != null &&
            userData.GetPassword().Length >= 8
        ) {
            string hashedPassword = passwordHasher.Hash(
                userData.GetPassword()
            );
            user.SetPassword(hashedPassword);
            user.SetEmail(userData.GetEmail());
            user.SetName(userData.GetName());
            
            user.Save();
            
            string verificationToken = GenerateRandomToken();
            user.SetVerificationToken(verificationToken);
            user.Save();
            
            string emailTemplate = "welcome_email";
            string emailContent = emailTemplateBuilder.Build(
                emailTemplate,
                user
            );
            
            emailService.Send(
                emailContent,
                user.GetEmail()
            );
            
            string logMessage = "User registered: " + user.GetEmail();
            logger.Info(logMessage);
        }
    }
  • fn register_user(user_data: &UserData) {
        if user_data.get_email().is_some() &&
           user_data.get_password().is_some() &&
           user_data.get_password().as_ref().unwrap().len() >= 8 {
            let hashed_password = password_hasher.hash(
                user_data.get_password().as_ref().unwrap()
            );
            user.set_password(&hashed_password);
            user.set_email(user_data.get_email().as_ref().unwrap());
            user.set_name(user_data.get_name().as_ref().unwrap());
            
            user.save();
            
            let verification_token = generate_random_token();
            user.set_verification_token(&verification_token);
            user.save();
            
            let email_template = "welcome_email";
            let email_content = email_template_builder.build(
                email_template,
                &user
            );
            
            email_service.send(
                &email_content,
                user.get_email()
            );
            
            let log_message = format!("User registered: {}", user.get_email());
            logger.info(&log_message);
        }
    }

The code above clearly shows that the register user method is handling too many responsibilities.

The following points represent different levels of abstraction that need to be extracted from the register user method:

  • Validating user input data.
  • Hashing the password securely.
  • Creating and saving the user entity.
  • Generating and storing the verification token.
  • Sending the welcome email.
  • Logging the registration event.

Let’s apply the SLA principle to improve the method.

  • def register_user
      if !self.user_data_valid()
        return
      end
      
      self.create_user()
      self.generate_verification_token()
      self.send_welcome_email()
      self.log_registration()
    end
    
    def user_data_valid
      return @user_data.get_email != nil &&
             @user_data.get_password != nil &&
             @user_data.get_password.length >= 8
    end
    
    def create_user
      hashed_password = @passwordHasher.hash(@user_data.get_password)
      @user.set_password(hashed_password)
      @user.set_email(@user_data.get_email)
      @user.set_name(@user_data.get_name)
      @user.save()
    end
    
    def generate_verification_token
      verification_token = SecureRandom.hex(32)
      @user.set_verification_token(verification_token)
      @user.save()
    end
    
    def send_welcome_email
      email_template = 'welcome_email'
      email_content = @emailTemplateBuilder.build(
          email_template,
          @user
      )
      @emailService.send(
          email_content,
          @user.get_email()
      )
    end
    
    def log_registration
      log_message = "User registered: " + @user.get_email()
      @logger.info(log_message)
    end
  • public function registerUser(): void
    {
        if (!$this->userDataValid()) {
            return;
        }
        
        $this->createUser();
        $this->generateVerificationToken();
        $this->sendWelcomeEmail();
        $this->logRegistration();
    }
    
    private function userDataValid(): bool
    {
        return $this->userData->getEmail() !== null &&
               $this->userData->getPassword() !== null &&
               strlen($this->userData->getPassword()) >= 8;
    }
    
    private function createUser(): void
    {
        $hashedPassword = $this->passwordHasher->hash(
            $this->userData->getPassword()
        );
        $this->user->setPassword($hashedPassword);
        $this->user->setEmail($this->userData->getEmail());
        $this->user->setName($this->userData->getName());
        $this->user->save();
    }
    
    private function generateVerificationToken(): void
    {
        $verificationToken = bin2hex(random_bytes(16));
        $this->user->setVerificationToken($verificationToken);
        $this->user->save();
    }
    
    private function sendWelcomeEmail(): void
    {
        $emailTemplate = 'welcome_email';
        $emailContent = $this->emailTemplateBuilder->build(
            $emailTemplate,
            $this->user
        );
        $this->emailService->send(
            $emailContent,
            $this->user->getEmail()
        );
    }
    
    private function logRegistration(): void
    {
        $logMessage = "User registered: " . $this->user->getEmail();
        $this->logger->info($logMessage);
    }
  • func RegisterUser(userData UserData) {
        if !UserDataValid(userData) {
            return
        }
        
        user := CreateUser(userData)
        GenerateVerificationToken(user)
        SendWelcomeEmail(user)
        LogRegistration(user)
    }
    
    func UserDataValid(userData UserData) bool {
        return userData.GetEmail() != "" &&
               userData.GetPassword() != "" &&
               len(userData.GetPassword()) >= 8
    }
    
    func CreateUser(userData UserData) User {
        hashedPassword := passwordHasher.Hash(
            userData.GetPassword(),
        )
        user.SetPassword(hashedPassword)
        user.SetEmail(userData.GetEmail())
        user.SetName(userData.GetName())
        user.Save()
        return user
    }
    
    func GenerateVerificationToken(user User) {
        verificationToken := generateRandomToken()
        user.SetVerificationToken(verificationToken)
        user.Save()
    }
    
    func SendWelcomeEmail(user User) {
        emailTemplate := "welcome_email"
        emailContent := emailTemplateBuilder.Build(
            emailTemplate,
            user,
        )
        emailService.Send(
            emailContent,
            user.GetEmail(),
        )
    }
    
    func LogRegistration(user User) {
        logMessage := "User registered: " + user.GetEmail()
        logger.Info(logMessage)
    }
  • def register_user(self):
        if not self.user_data_valid():
            return
        
        self.create_user()
        self.generate_verification_token()
        self.send_welcome_email()
        self.log_registration()
    
    def user_data_valid(self):
        return (self.user_data.get_email() is not None and
                self.user_data.get_password() is not None and
                len(self.user_data.get_password()) >= 8)
    
    def create_user(self):
        hashed_password = self.password_hasher.hash(
            self.user_data.get_password()
        )
        self.user.set_password(hashed_password)
        self.user.set_email(self.user_data.get_email())
        self.user.set_name(self.user_data.get_name())
        self.user.save()
    
    def generate_verification_token(self):
        verification_token = secrets.token_hex(16)
        self.user.set_verification_token(verification_token)
        self.user.save()
    
    def send_welcome_email(self):
        email_template = 'welcome_email'
        email_content = self.email_template_builder.build(
            email_template,
            self.user
        )
        self.email_service.send(
            email_content,
            self.user.get_email()
        )
    
    def log_registration(self):
        log_message = "User registered: " + self.user.get_email()
        self.logger.info(log_message)
  • registerUser() {
        if (!this.userDataValid()) {
            return;
        }
        
        this.createUser();
        this.generateVerificationToken();
        this.sendWelcomeEmail();
        this.logRegistration();
    }
    
    userDataValid() {
        return this.userData.getEmail() !== null &&
               this.userData.getPassword() !== null &&
               this.userData.getPassword().length >= 8;
    }
    
    createUser() {
        const hashedPassword = this.passwordHasher.hash(
            this.userData.getPassword()
        );
        this.user.setPassword(hashedPassword);
        this.user.setEmail(this.userData.getEmail());
        this.user.setName(this.userData.getName());
        this.user.save();
    }
    
    generateVerificationToken() {
        const verificationToken = crypto.randomBytes(16).toString('hex');
        this.user.setVerificationToken(verificationToken);
        this.user.save();
    }
    
    sendWelcomeEmail() {
        const emailTemplate = 'welcome_email';
        const emailContent = this.emailTemplateBuilder.build(
            emailTemplate,
            this.user
        );
        this.emailService.send(
            emailContent,
            this.user.getEmail()
        );
    }
    
    logRegistration() {
        const logMessage = "User registered: " + this.user.getEmail();
        this.logger.info(logMessage);
    }
  • public void registerUser(UserData userData)
    {
        if (!userDataValid(userData)) {
            return;
        }
        
        User user = createUser(userData);
        generateVerificationToken(user);
        sendWelcomeEmail(user);
        logRegistration(user);
    }
    
    private boolean userDataValid(UserData userData)
    {
        return userData.getEmail() != null &&
               userData.getPassword() != null &&
               userData.getPassword().length() >= 8;
    }
    
    private User createUser(UserData userData)
    {
        String hashedPassword = passwordHasher.hash(
            userData.getPassword()
        );
        User user = new User();
        user.setPassword(hashedPassword);
        user.setEmail(userData.getEmail());
        user.setName(userData.getName());
        user.save();
        return user;
    }
    
    private void generateVerificationToken(User user)
    {
        String verificationToken = generateRandomToken();
        user.setVerificationToken(verificationToken);
        user.save();
    }
    
    private void sendWelcomeEmail(User user)
    {
        String emailTemplate = "welcome_email";
        String emailContent = emailTemplateBuilder.build(
            emailTemplate,
            user
        );
        emailService.send(
            emailContent,
            user.getEmail()
        );
    }
    
    private void logRegistration(User user)
    {
        String logMessage = "User registered: " + user.getEmail();
        logger.info(logMessage);
    }
  • void RegisterUser(UserData userData)
    {
        if (!UserDataValid(userData)) {
            return;
        }
        
        User user = CreateUser(userData);
        GenerateVerificationToken(user);
        SendWelcomeEmail(user);
        LogRegistration(user);
    }
    
    bool UserDataValid(UserData userData)
    {
        return userData.GetEmail() != null &&
               userData.GetPassword() != null &&
               userData.GetPassword().Length >= 8;
    }
    
    User CreateUser(UserData userData)
    {
        string hashedPassword = passwordHasher.Hash(
            userData.GetPassword()
        );
        User user = new User();
        user.SetPassword(hashedPassword);
        user.SetEmail(userData.GetEmail());
        user.SetName(userData.GetName());
        user.Save();
        return user;
    }
    
    void GenerateVerificationToken(User user)
    {
        string verificationToken = GenerateRandomToken();
        user.SetVerificationToken(verificationToken);
        user.Save();
    }
    
    void SendWelcomeEmail(User user)
    {
        string emailTemplate = "welcome_email";
        string emailContent = emailTemplateBuilder.Build(
            emailTemplate,
            user
        );
        emailService.Send(
            emailContent,
            user.GetEmail()
        );
    }
    
    void LogRegistration(User user)
    {
        string logMessage = "User registered: " + user.GetEmail();
        logger.Info(logMessage);
    }
  • fn register_user(user_data: &UserData) {
        if !user_data_valid(user_data) {
            return;
        }
        
        let mut user = create_user(user_data);
        generate_verification_token(&mut user);
        send_welcome_email(&user);
        log_registration(&user);
    }
    
    fn user_data_valid(user_data: &UserData) -> bool {
        user_data.get_email().is_some() &&
        user_data.get_password().is_some() &&
        user_data.get_password().as_ref().unwrap().len() >= 8
    }
    
    fn create_user(user_data: &UserData) -> User {
        let hashed_password = password_hasher.hash(
            user_data.get_password().as_ref().unwrap()
        );
        let mut user = User::new();
        user.set_password(&hashed_password);
        user.set_email(user_data.get_email().as_ref().unwrap());
        user.set_name(user_data.get_name().as_ref().unwrap());
        user.save();
        user
    }
    
    fn generate_verification_token(user: &mut User) {
        let verification_token = generate_random_token();
        user.set_verification_token(&verification_token);
        user.save();
    }
    
    fn send_welcome_email(user: &User) {
        let email_template = "welcome_email";
        let email_content = email_template_builder.build(
            email_template,
            user
        );
        email_service.send(
            &email_content,
            user.get_email()
        );
    }
    
    fn log_registration(user: &User) {
        let log_message = format!("User registered: {}", user.get_email());
        logger.info(&log_message);
    }

Much better! The register user method now clearly shows each step at the same level of abstraction.

Payment Processing

In this example, we will demonstrate the principle using a payment processing feature. This will illustrate how to properly separate concerns when processing a customer payment.

ℹ️ Note: Code examples in this article were syntax-checked and completed with AI assistance to ensure correctness across multiple programming languages. ℹ️
  • def process_payment
      if @payment.get_amount > 0 && 
         @payment.get_currency != nil &&
         @payment.get_card_number != nil
        
        _card_valid = @cardValidator.validate(@payment.get_card_number)
        if _card_valid
          _encrypted_card = @encryptionService.encrypt(@payment.get_card_number)
          
          _payment_gateway_response = @paymentGateway.charge(
              @payment.get_amount,
              @payment.get_currency,
              _encrypted_card
          )
          
          if _payment_gateway_response.get_status == 'success'
            @payment.set_status('completed')
            @payment.set_transaction_id(_payment_gateway_response.get_transaction_id)
            @payment.save()
            
            _receipt_template = 'payment_receipt'
            _receipt_content = @emailTemplateBuilder.build(
                _receipt_template,
                @payment
            )
            
            @emailService.send(
                _receipt_content,
                @payment.get_customer_email()
            )
            
            _log_entry = "Payment processed: " + @payment.get_transaction_id
            @logger.info(_log_entry)
          else
            @payment.set_status('failed')
            @payment.save()
          end
        end
      end
    end
  • public function processPayment(): void
    {
        if (
            $this->payment->getAmount() > 0 &&
            $this->payment->getCurrency() !== null &&
            $this->payment->getCardNumber() !== null
        ) {
            $cardValid = $this->cardValidator->validate(
                $this->payment->getCardNumber()
            );
            
            if ($cardValid) {
                $encryptedCard = $this->encryptionService->encrypt(
                    $this->payment->getCardNumber()
                );
                
                $paymentGatewayResponse = $this->paymentGateway->charge(
                    $this->payment->getAmount(),
                    $this->payment->getCurrency(),
                    $encryptedCard
                );
                
                if ($paymentGatewayResponse->getStatus() === 'success') {
                    $this->payment->setStatus('completed');
                    $this->payment->setTransactionId(
                        $paymentGatewayResponse->getTransactionId()
                    );
                    $this->payment->save();
                    
                    $receiptTemplate = 'payment_receipt';
                    $receiptContent = $this->emailTemplateBuilder->build(
                        $receiptTemplate,
                        $this->payment
                    );
                    
                    $this->emailService->send(
                        $receiptContent,
                        $this->payment->getCustomerEmail()
                    );
                    
                    $logEntry = "Payment processed: " . 
                               $this->payment->getTransactionId();
                    $this->logger->info($logEntry);
                } else {
                    $this->payment->setStatus('failed');
                    $this->payment->save();
                }
            }
        }
    }
  • func ProcessPayment(payment Payment) {
        if payment.GetAmount() > 0 &&
           payment.GetCurrency() != "" &&
           payment.GetCardNumber() != "" {
            cardValid := cardValidator.Validate(
                payment.GetCardNumber(),
            )
            
            if cardValid {
                encryptedCard := encryptionService.Encrypt(
                    payment.GetCardNumber(),
                )
                
                paymentGatewayResponse := paymentGateway.Charge(
                    payment.GetAmount(),
                    payment.GetCurrency(),
                    encryptedCard,
                )
                
                if paymentGatewayResponse.GetStatus() == "success" {
                    payment.SetStatus("completed")
                    payment.SetTransactionId(
                        paymentGatewayResponse.GetTransactionId(),
                    )
                    payment.Save()
                    
                    receiptTemplate := "payment_receipt"
                    receiptContent := emailTemplateBuilder.Build(
                        receiptTemplate,
                        payment,
                    )
                    
                    emailService.Send(
                        receiptContent,
                        payment.GetCustomerEmail(),
                    )
                    
                    logEntry := "Payment processed: " + 
                               payment.GetTransactionId()
                    logger.Info(logEntry)
                } else {
                    payment.SetStatus("failed")
                    payment.Save()
                }
            }
        }
    }
  • def process_payment(self):
        if (self.payment.get_amount() > 0 and
           self.payment.get_currency() is not None and
           self.payment.get_card_number() is not None):
            card_valid = self.card_validator.validate(
                self.payment.get_card_number()
            )
            
            if card_valid:
                encrypted_card = self.encryption_service.encrypt(
                    self.payment.get_card_number()
                )
                
                payment_gateway_response = self.payment_gateway.charge(
                    self.payment.get_amount(),
                    self.payment.get_currency(),
                    encrypted_card
                )
                
                if payment_gateway_response.get_status() == 'success':
                    self.payment.set_status('completed')
                    self.payment.set_transaction_id(
                        payment_gateway_response.get_transaction_id()
                    )
                    self.payment.save()
                    
                    receipt_template = 'payment_receipt'
                    receipt_content = self.email_template_builder.build(
                        receipt_template,
                        self.payment
                    )
                    
                    self.email_service.send(
                        receipt_content,
                        self.payment.get_customer_email()
                    )
                    
                    log_entry = "Payment processed: " + self.payment.get_transaction_id()
                    self.logger.info(log_entry)
                else:
                    self.payment.set_status('failed')
                    self.payment.save()
  • processPayment() {
        if (
            this.payment.getAmount() > 0 &&
            this.payment.getCurrency() !== null &&
            this.payment.getCardNumber() !== null
        ) {
            const cardValid = this.cardValidator.validate(
                this.payment.getCardNumber()
            );
            
            if (cardValid) {
                const encryptedCard = this.encryptionService.encrypt(
                    this.payment.getCardNumber()
                );
                
                const paymentGatewayResponse = this.paymentGateway.charge(
                    this.payment.getAmount(),
                    this.payment.getCurrency(),
                    encryptedCard
                );
                
                if (paymentGatewayResponse.getStatus() === 'success') {
                    this.payment.setStatus('completed');
                    this.payment.setTransactionId(
                        paymentGatewayResponse.getTransactionId()
                    );
                    this.payment.save();
                    
                    const receiptTemplate = 'payment_receipt';
                    const receiptContent = this.emailTemplateBuilder.build(
                        receiptTemplate,
                        this.payment
                    );
                    
                    this.emailService.send(
                        receiptContent,
                        this.payment.getCustomerEmail()
                    );
                    
                    const logEntry = "Payment processed: " + 
                                    this.payment.getTransactionId();
                    this.logger.info(logEntry);
                } else {
                    this.payment.setStatus('failed');
                    this.payment.save();
                }
            }
        }
    }
  • public void processPayment(Payment payment) 
    {
        if (
            payment.getAmount() > 0 &&
            payment.getCurrency() != null &&
            payment.getCardNumber() != null
        ) {
            boolean cardValid = cardValidator.validate(
                payment.getCardNumber()
            );
            
            if (cardValid) {
                String encryptedCard = encryptionService.encrypt(
                    payment.getCardNumber()
                );
                
                PaymentGatewayResponse response = paymentGateway.charge(
                    payment.getAmount(),
                    payment.getCurrency(),
                    encryptedCard
                );
                
                if (response.getStatus().equals("success")) {
                    payment.setStatus("completed");
                    payment.setTransactionId(
                        response.getTransactionId()
                    );
                    payment.save();
                    
                    String receiptTemplate = "payment_receipt";
                    String receiptContent = emailTemplateBuilder.build(
                        receiptTemplate,
                        payment
                    );
                    
                    emailService.send(
                        receiptContent,
                        payment.getCustomerEmail()
                    );
                    
                    String logEntry = "Payment processed: " + 
                                    payment.getTransactionId();
                    logger.info(logEntry);
                } else {
                    payment.setStatus("failed");
                    payment.save();
                }
            }
        }
    }
  • void ProcessPayment(Payment payment) 
    {
        if (
            payment.GetAmount() > 0 &&
            payment.GetCurrency() != null &&
            payment.GetCardNumber() != null
        ) {
            bool cardValid = cardValidator.Validate(
                payment.GetCardNumber()
            );
            
            if (cardValid) {
                string encryptedCard = encryptionService.Encrypt(
                    payment.GetCardNumber()
                );
                
                PaymentGatewayResponse response = paymentGateway.Charge(
                    payment.GetAmount(),
                    payment.GetCurrency(),
                    encryptedCard
                );
                
                if (response.GetStatus() == "success") {
                    payment.SetStatus("completed");
                    payment.SetTransactionId(
                        response.GetTransactionId()
                    );
                    payment.Save();
                    
                    string receiptTemplate = "payment_receipt";
                    string receiptContent = emailTemplateBuilder.Build(
                        receiptTemplate,
                        payment
                    );
                    
                    emailService.Send(
                        receiptContent,
                        payment.GetCustomerEmail()
                    );
                    
                    string logEntry = "Payment processed: " + 
                                     payment.GetTransactionId();
                    logger.Info(logEntry);
                } else {
                    payment.SetStatus("failed");
                    payment.Save();
                }
            }
        }
    }
  • fn process_payment(payment: &mut Payment) {
        if payment.get_amount() > 0.0 &&
           payment.get_currency().is_some() &&
           payment.get_card_number().is_some() {
            let card_valid = card_validator.validate(
                payment.get_card_number().as_ref().unwrap()
            );
            
            if card_valid {
                let encrypted_card = encryption_service.encrypt(
                    payment.get_card_number().as_ref().unwrap()
                );
                
                let payment_gateway_response = payment_gateway.charge(
                    payment.get_amount(),
                    payment.get_currency().as_ref().unwrap(),
                    &encrypted_card
                );
                
                if payment_gateway_response.get_status() == "success" {
                    payment.set_status("completed");
                    payment.set_transaction_id(
                        payment_gateway_response.get_transaction_id()
                    );
                    payment.save();
                    
                    let receipt_template = "payment_receipt";
                    let receipt_content = email_template_builder.build(
                        receipt_template,
                        payment
                    );
                    
                    email_service.send(
                        &receipt_content,
                        payment.get_customer_email()
                    );
                    
                    let log_entry = format!(
                        "Payment processed: {}", 
                        payment.get_transaction_id()
                    );
                    logger.info(&log_entry);
                } else {
                    payment.set_status("failed");
                    payment.save();
                }
            }
        }
    }

The code above demonstrates that the process payment method is handling too many responsibilities at different abstraction levels.

The following points represent different levels of abstraction that need to be extracted from the process payment method:

  • Validating payment data and card information.
  • Encrypting sensitive card data.
  • Processing the payment through the gateway.
  • Updating payment status based on the result.
  • Sending payment receipt email.
  • Logging the payment transaction.

Let’s apply the SLA principle to improve the method.

  • def process_payment
      if !self.payment_valid()
        return
      end
      
      if !self.card_valid()
        return
      end
      
      if self.charge_payment()
        self.complete_payment()
        self.send_receipt()
        self.log_payment()
      else
        self.fail_payment()
      end
    end
    
    def payment_valid
      return @payment.get_amount > 0 &&
             @payment.get_currency != nil &&
             @payment.get_card_number != nil
    end
    
    def card_valid
      return @cardValidator.validate(@payment.get_card_number)
    end
    
    def charge_payment
      encrypted_card = @encryptionService.encrypt(@payment.get_card_number)
      response = @paymentGateway.charge(
          @payment.get_amount,
          @payment.get_currency,
          encrypted_card
      )
      return response.get_status == 'success'
    end
    
    def complete_payment
      @payment.set_status('completed')
      @payment.set_transaction_id(@payment_gateway_response.get_transaction_id)
      @payment.save()
    end
    
    def send_receipt
      receipt_template = 'payment_receipt'
      receipt_content = @emailTemplateBuilder.build(
          receipt_template,
          @payment
      )
      @emailService.send(
          receipt_content,
          @payment.get_customer_email()
      )
    end
    
    def log_payment
      log_entry = "Payment processed: " + @payment.get_transaction_id
      @logger.info(log_entry)
    end
    
    def fail_payment
      @payment.set_status('failed')
      @payment.save()
    end
  • public function processPayment(): void
    {
        if (!$this->paymentValid()) {
            return;
        }
        
        if (!$this->cardValid()) {
            return;
        }
        
        if ($this->chargePayment()) {
            $this->completePayment();
            $this->sendReceipt();
            $this->logPayment();
        } else {
            $this->failPayment();
        }
    }
    
    private function paymentValid(): bool
    {
        return $this->payment->getAmount() > 0 &&
               $this->payment->getCurrency() !== null &&
               $this->payment->getCardNumber() !== null;
    }
    
    private function cardValid(): bool
    {
        return $this->cardValidator->validate(
            $this->payment->getCardNumber()
        );
    }
    
    private function chargePayment(): bool
    {
        $encryptedCard = $this->encryptionService->encrypt(
            $this->payment->getCardNumber()
        );
        $response = $this->paymentGateway->charge(
            $this->payment->getAmount(),
            $this->payment->getCurrency(),
            $encryptedCard
        );
        $this->paymentGatewayResponse = $response;
        return $response->getStatus() === 'success';
    }
    
    private function completePayment(): void
    {
        $this->payment->setStatus('completed');
        $this->payment->setTransactionId(
            $this->paymentGatewayResponse->getTransactionId()
        );
        $this->payment->save();
    }
    
    private function sendReceipt(): void
    {
        $receiptTemplate = 'payment_receipt';
        $receiptContent = $this->emailTemplateBuilder->build(
            $receiptTemplate,
            $this->payment
        );
        $this->emailService->send(
            $receiptContent,
            $this->payment->getCustomerEmail()
        );
    }
    
    private function logPayment(): void
    {
        $logEntry = "Payment processed: " . 
                   $this->payment->getTransactionId();
        $this->logger->info($logEntry);
    }
    
    private function failPayment(): void
    {
        $this->payment->setStatus('failed');
        $this->payment->save();
    }
  • func ProcessPayment(payment Payment) {
        if !PaymentValid(payment) {
            return
        }
        
        if !CardValid(payment) {
            return
        }
        
        if ChargePayment(payment) {
            CompletePayment(payment)
            SendReceipt(payment)
            LogPayment(payment)
        } else {
            FailPayment(payment)
        }
    }
    
    func PaymentValid(payment Payment) bool {
        return payment.GetAmount() > 0 &&
               payment.GetCurrency() != "" &&
               payment.GetCardNumber() != ""
    }
    
    func CardValid(payment Payment) bool {
        return cardValidator.Validate(
            payment.GetCardNumber(),
        )
    }
    
    func ChargePayment(payment Payment) bool {
        encryptedCard := encryptionService.Encrypt(
            payment.GetCardNumber(),
        )
        response := paymentGateway.Charge(
            payment.GetAmount(),
            payment.GetCurrency(),
            encryptedCard,
        )
        payment.SetGatewayResponse(response)
        return response.GetStatus() == "success"
    }
    
    func CompletePayment(payment Payment) {
        payment.SetStatus("completed")
        payment.SetTransactionId(
            payment.GetGatewayResponse().GetTransactionId(),
        )
        payment.Save()
    }
    
    func SendReceipt(payment Payment) {
        receiptTemplate := "payment_receipt"
        receiptContent := emailTemplateBuilder.Build(
            receiptTemplate,
            payment,
        )
        emailService.Send(
            receiptContent,
            payment.GetCustomerEmail(),
        )
    }
    
    func LogPayment(payment Payment) {
        logEntry := "Payment processed: " + payment.GetTransactionId()
        logger.Info(logEntry)
    }
    
    func FailPayment(payment Payment) {
        payment.SetStatus("failed")
        payment.Save()
    }
  • def process_payment(self):
        if not self.payment_valid():
            return
        
        if not self.card_valid():
            return
        
        if self.charge_payment():
            self.complete_payment()
            self.send_receipt()
            self.log_payment()
        else:
            self.fail_payment()
    
    def payment_valid(self):
        return (self.payment.get_amount() > 0 and
                self.payment.get_currency() is not None and
                self.payment.get_card_number() is not None)
    
    def card_valid(self):
        return self.card_validator.validate(
            self.payment.get_card_number()
        )
    
    def charge_payment(self):
        encrypted_card = self.encryption_service.encrypt(
            self.payment.get_card_number()
        )
        response = self.payment_gateway.charge(
            self.payment.get_amount(),
            self.payment.get_currency(),
            encrypted_card
        )
        self.payment_gateway_response = response
        return response.get_status() == 'success'
    
    def complete_payment(self):
        self.payment.set_status('completed')
        self.payment.set_transaction_id(
            self.payment_gateway_response.get_transaction_id()
        )
        self.payment.save()
    
    def send_receipt(self):
        receipt_template = 'payment_receipt'
        receipt_content = self.email_template_builder.build(
            receipt_template,
            self.payment
        )
        self.email_service.send(
            receipt_content,
            self.payment.get_customer_email()
        )
    
    def log_payment(self):
        log_entry = "Payment processed: " + self.payment.get_transaction_id()
        self.logger.info(log_entry)
    
    def fail_payment(self):
        self.payment.set_status('failed')
        self.payment.save()
  • processPayment() {
        if (!this.paymentValid()) {
            return;
        }
        
        if (!this.cardValid()) {
            return;
        }
        
        if (this.chargePayment()) {
            this.completePayment();
            this.sendReceipt();
            this.logPayment();
        } else {
            this.failPayment();
        }
    }
    
    paymentValid() {
        return this.payment.getAmount() > 0 &&
               this.payment.getCurrency() !== null &&
               this.payment.getCardNumber() !== null;
    }
    
    cardValid() {
        return this.cardValidator.validate(
            this.payment.getCardNumber()
        );
    }
    
    chargePayment() {
        const encryptedCard = this.encryptionService.encrypt(
            this.payment.getCardNumber()
        );
        const response = this.paymentGateway.charge(
            this.payment.getAmount(),
            this.payment.getCurrency(),
            encryptedCard
        );
        this.paymentGatewayResponse = response;
        return response.getStatus() === 'success';
    }
    
    completePayment() {
        this.payment.setStatus('completed');
        this.payment.setTransactionId(
            this.paymentGatewayResponse.getTransactionId()
        );
        this.payment.save();
    }
    
    sendReceipt() {
        const receiptTemplate = 'payment_receipt';
        const receiptContent = this.emailTemplateBuilder.build(
            receiptTemplate,
            this.payment
        );
        this.emailService.send(
            receiptContent,
            this.payment.getCustomerEmail()
        );
    }
    
    logPayment() {
        const logEntry = "Payment processed: " + 
                        this.payment.getTransactionId();
        this.logger.info(logEntry);
    }
    
    failPayment() {
        this.payment.setStatus('failed');
        this.payment.save();
    }
  • public void processPayment(Payment payment)
    {
        if (!paymentValid(payment)) {
            return;
        }
        
        if (!cardValid(payment)) {
            return;
        }
        
        if (chargePayment(payment)) {
            completePayment(payment);
            sendReceipt(payment);
            logPayment(payment);
        } else {
            failPayment(payment);
        }
    }
    
    private boolean paymentValid(Payment payment)
    {
        return payment.getAmount() > 0 &&
               payment.getCurrency() != null &&
               payment.getCardNumber() != null;
    }
    
    private boolean cardValid(Payment payment)
    {
        return cardValidator.validate(
            payment.getCardNumber()
        );
    }
    
    private boolean chargePayment(Payment payment)
    {
        String encryptedCard = encryptionService.encrypt(
            payment.getCardNumber()
        );
        PaymentGatewayResponse response = paymentGateway.charge(
            payment.getAmount(),
            payment.getCurrency(),
            encryptedCard
        );
        payment.setGatewayResponse(response);
        return response.getStatus().equals("success");
    }
    
    private void completePayment(Payment payment)
    {
        payment.setStatus("completed");
        payment.setTransactionId(
            payment.getGatewayResponse().getTransactionId()
        );
        payment.save();
    }
    
    private void sendReceipt(Payment payment)
    {
        String receiptTemplate = "payment_receipt";
        String receiptContent = emailTemplateBuilder.build(
            receiptTemplate,
            payment
        );
        emailService.send(
            receiptContent,
            payment.getCustomerEmail()
        );
    }
    
    private void logPayment(Payment payment)
    {
        String logEntry = "Payment processed: " + 
                         payment.getTransactionId();
        logger.info(logEntry);
    }
    
    private void failPayment(Payment payment)
    {
        payment.setStatus("failed");
        payment.save();
    }
  • void ProcessPayment(Payment payment)
    {
        if (!PaymentValid(payment)) {
            return;
        }
        
        if (!CardValid(payment)) {
            return;
        }
        
        if (ChargePayment(payment)) {
            CompletePayment(payment);
            SendReceipt(payment);
            LogPayment(payment);
        } else {
            FailPayment(payment);
        }
    }
    
    bool PaymentValid(Payment payment)
    {
        return payment.GetAmount() > 0 &&
               payment.GetCurrency() != null &&
               payment.GetCardNumber() != null;
    }
    
    bool CardValid(Payment payment)
    {
        return cardValidator.Validate(
            payment.GetCardNumber()
        );
    }
    
    bool ChargePayment(Payment payment)
    {
        string encryptedCard = encryptionService.Encrypt(
            payment.GetCardNumber()
        );
        PaymentGatewayResponse response = paymentGateway.Charge(
            payment.GetAmount(),
            payment.GetCurrency(),
            encryptedCard
        );
        payment.SetGatewayResponse(response);
        return response.GetStatus() == "success";
    }
    
    void CompletePayment(Payment payment)
    {
        payment.SetStatus("completed");
        payment.SetTransactionId(
            payment.GetGatewayResponse().GetTransactionId()
        );
        payment.Save();
    }
    
    void SendReceipt(Payment payment)
    {
        string receiptTemplate = "payment_receipt";
        string receiptContent = emailTemplateBuilder.Build(
            receiptTemplate,
            payment
        );
        emailService.Send(
            receiptContent,
            payment.GetCustomerEmail()
        );
    }
    
    void LogPayment(Payment payment)
    {
        string logEntry = "Payment processed: " + 
                         payment.GetTransactionId();
        logger.Info(logEntry);
    }
    
    void FailPayment(Payment payment)
    {
        payment.SetStatus("failed");
        payment.Save();
    }
  • fn process_payment(payment: &mut Payment) {
        if !payment_valid(payment) {
            return;
        }
        
        if !card_valid(payment) {
            return;
        }
        
        if charge_payment(payment) {
            complete_payment(payment);
            send_receipt(payment);
            log_payment(payment);
        } else {
            fail_payment(payment);
        }
    }
    
    fn payment_valid(payment: &Payment) -> bool {
        payment.get_amount() > 0.0 &&
        payment.get_currency().is_some() &&
        payment.get_card_number().is_some()
    }
    
    fn card_valid(payment: &Payment) -> bool {
        card_validator.validate(
            payment.get_card_number().as_ref().unwrap()
        )
    }
    
    fn charge_payment(payment: &mut Payment) -> bool {
        let encrypted_card = encryption_service.encrypt(
            payment.get_card_number().as_ref().unwrap()
        );
        let response = payment_gateway.charge(
            payment.get_amount(),
            payment.get_currency().as_ref().unwrap(),
            &encrypted_card
        );
        payment.set_gateway_response(response.clone());
        response.get_status() == "success"
    }
    
    fn complete_payment(payment: &mut Payment) {
        payment.set_status("completed");
        payment.set_transaction_id(
            payment.get_gateway_response().get_transaction_id()
        );
        payment.save();
    }
    
    fn send_receipt(payment: &Payment) {
        let receipt_template = "payment_receipt";
        let receipt_content = email_template_builder.build(
            receipt_template,
            payment
        );
        email_service.send(
            &receipt_content,
            payment.get_customer_email()
        );
    }
    
    fn log_payment(payment: &Payment) {
        let log_entry = format!(
            "Payment processed: {}", 
            payment.get_transaction_id()
        );
        logger.info(&log_entry);
    }
    
    fn fail_payment(payment: &mut Payment) {
        payment.set_status("failed");
        payment.save();
    }

Excellent! The process payment method now clearly shows each step at the same level of abstraction, making it much easier to understand and maintain.

Written on December 7, 2025