0x7E5 Reasoning about the main thing

About myself

Greetings to all. My name is Vyacheslav, I have been working in IT for 11 years in the direction of Android. I touched and stroked dinosaurs in the face of Android 1.5 and 1.6, went through all the stages of the formation of MVP MVVM Retrofit and many other libraries. Looked at my old code as a bunch of g ... many times and still keep learning and developing. I managed to learn more than a dozen, I'm not afraid of this word, “strong” guys, with good potential and a head on their shoulders, in the process of training, rules and recommendations were formed, which I want to share. 





About article

Recently, I have come across many projects of varying complexity and I see a natural problem. Novice programmers do not see the value of concepts like Clean Code, KISS and SOLID. We can agree that Clean Code is far from being for beginners, but I think that in general terms, knowledge of this approach is necessary. Intermediate programmers do not fully apply these approaches. Experienced programmers often go too deep into details and forget about the most important things. For beginners: This article will help you collect rules for yourself that are worth paying attention to. 





For the Experienced: Redefine your views or delve into the details of modern coding approaches.





For professionals: look at modern approaches from a different angle (hopefully). Sometimes it's helpful to take a step back and make sure you're on the right track.





I will not go into all aspects of development, more time will be devoted to the very ideas and rules that should be paid attention to during development. I will touch upon some modern libraries and solutions in the field of reactive programming. I will give my opinion on architectures and Clean Code.





Approaches

Clean Code

So, I propose to begin with talking about modern approaches, what is meant by them and what is really important. 





“Clean Code”. , . . - , ( ). - , . : “ ”. - 2 , - - ”. , - . . , 100 . . 2 ? - , - , . .. - ,  . “transformDateToString” “transDTS”. “ ” - “”, “ ” - . “” “”, “” . . , . : - .





KISS 

KISS (keep it simple, stupid). , . - , , , . . SOLID, , , , - , . . 





    interface Factory<out T> {
       fun create(): T
    }
    typealias PrinterFun = (String) -> Unit

    interface PrinterFactory : Factory<PrinterFun>
    interface MessageFactory : Factory<String>
    interface MessagePrinter {
       fun print(pf: PrinterFactory, mf: MessageFactory)
    }

    class PrinterFactoryImpl : PrinterFactory {
       override fun create(): PrinterFun = ::print
    }

    class MessageFactoryImpl : MessageFactory {
   companion object {
       const val DEFAULT_MESSAGE = "Hello World"
   }

   override fun create(): String = DEFAULT_MESSAGE


   class MessagePrinterImpl : MessagePrinter {
       override fun print(pf: PrinterFactory, mf: MessageFactory) {
           pf.create().invoke(mf.create())
       }
   }

   class ImplProvider {
       private val impls = HashMap<KClass<out Any>, Any>()
       fun <T : Any> setImpl(clazz: KClass<T>, t: T) {
           impls[clazz] = t
       }

       fun <T : Any> getImpl(clazz: KClass<T>): T {
           return (impls[clazz] as? T) ?: throw Exception("No impl")
       }
   }

   fun main(args: Array<String>) {
       val implProvider = ImplProvider()
       implProvider.setImpl(PrinterFactory::class, PrinterFactoryImpl())
       implProvider.setImpl(MessageFactory::class, MessageFactoryImpl())
       implProvider.setImpl(MessagePrinter::class, MessagePrinterImpl())

       implProvider.getImpl(MessagePrinter::class)
               .print(implProvider.getImpl(PrinterFactory::class),
                       implProvider.getImpl(MessageFactory::class))
   }
      
      



“Hello world”? “” - . 





    class TimeFormatter {
       private val timeFormat = SimpleDateFormat("HH:mm:ss", Locale.getDefault())
       fun formatTime() = timeFormat.format(Date())
    }
      
      



. (UNIT ) . , . .





SOLID

“” : SOLID! , , - . . , - , - , , . - , . “ ”, “ ”, .





S - single responsibility

[WIKI] (single responsibility principle). . , , .





: “ ”.





“ ”, - , , . 20 1-2 (TextEditUtils, TextTransformUtils, TextConcatUtils ) - TextUtils . , , . GOD-CLASS . , , . . - . , - . , - ( , ). 





O - open–closed

[WIKI] /. « … , ».





“ ( )”. , , . 





. “ ?”. - , ? , - , . . - - , . :





open class UiComponent() {
   var mode : Int = 0
   fun showTextAndImage(text:String, image: Image){
       mode = 0
       ...
   }
   fun showButton(text:String, action: Runnable){
       mode = 1
       ...
   }
   ...
}
      
      



, -:





class MyUiComponent(): UiComponent(){
   fun doMagic(){mode = 3}
}
      
      



- “ ”, “? , ?” . , , , , , . “mode” , - . , ( ), .





, , , , .





L - Liskov substitution

[WIKI] . « ». .





, . , . . “ ”.  , , , . . 





. . “Downloader” “downloadFile(url)”. , , . “Downloader” , . ( ) Downloadable:





class DownloadManager() {
   fun download(downloadable: Downloadable) {
       val stream = downloadable.openStream()
       val file = File(downloadable.getFileName())
       //   
   }
}

interface Downloadable {
   fun openStream(): InputStream
   fun getFileName(): String
}

class SimpleDownloadableFile(val name: String, 
                             val url: String) : Downloadable {
   override fun openStream() = URL(url).openStream()
   override fun getFileName() = name
}

class HeaderFile(val name: String, 
                 val url: String, 
                 val headers: Map<String, String>) : Downloadable {
   override fun openStream(): InputStream { 
            /*    */ 
   }
   override fun getFileName() = name
}
      
      



( ) ( , + ) 





- , . . - : 





interface Something
interface SomethingSpecific : Something
interface WritableSomething : SomethingSpecific {
   fun writeToFile()
}

interface GetableWritable<T> : WritableSomething {
   fun obtain(): T
}

abstract class ObtainableFile(val name: String) : GetableWritable<File> {
   override fun obtain() = File(name)
   override fun writeToFile() = obtain().write(getStream())
   abstract fun getStream(): InputStream
}

class UrlFile(url: String, name: String) : ObtainableFile(name) {
   override fun getStream(): InputStream = URL(url).openStream()
}
      
      



, . , KISS.  PS: …





I - interface segregation

[WIKI] . « , , , ».





. - “ ” . - “”, “interface / abstract class”. 





“” “ ” . 1000 , , java interface, . 





“” - , . ( Utils) . . “ ”, .





. “” “”, S (Single responsibility). Open-close . Liskov substitution .





D - dependency inversion

[WIKI] . « . - ».





, , . . . , . : single responsibility, ( , , , ).  : . , Liskov substitution “”, ,  . 





: . :





open class ServerManager {
   open fun getData(): String = "  "
}

open class CacheManager {
   open fun saveData(data: String) {/*  /  */}
}

class DataManager{
   fun getDataAndCache(){
       val data = ServerManager().getData()
       CacheManager().saveData(data)
   }
}
      
      



, / “”, .





- . DataManager : 





class DataManager(private val serverManager: ServerManager,
                  private val cacheManager: CacheManager) {
   fun getDataAndCache() {
       val data = serverManager.getData()
       cacheManager.saveData(data)
   }
}
      
      



. , ( , - ). 





Clean Architecture , . “” :





interface ServerManager {
   fun getData(): String
}
open class ServerManagerImpl : ServerManager {
   override fun getData(): String = "  "
}

interface CacheManager {
   fun saveData(data: String)
}
open class CacheManagerImpl : CacheManager {
   override fun saveData(data: String) {
       /*  /  */
   }
}

interface DataManager {
   fun getDataAndCache()
}
class DataManagerImpl(
       private val serverManager: ServerManager,
       private val cacheManager: CacheManager,
) : DataManager {
   override fun getDataAndCache() {
       val data = serverManager.getData()
       cacheManager.saveData(data)
   }
}

fun foo(){
   val dataManager: DataManager = DataManagerImpl(
           ServerManagerImpl(),
           CacheManagerImpl()
   )
   dataManager.getDataAndCache()
}
      
      



( ) , ( ). 





(Dagger, Koin, ServiceLocator ), . , , :





interface TextProvider {
   fun getText(): String
}

class SimpleTextProvider(private val text: String) : TextProvider {
   override fun getText(): String = text
}

class Printer(private val textProvider: TextProvider) {
   fun printText() {
       println(textProvider.getText())
   }
}

fun main() {
   Printer(SimpleTextProvider("text")).printText()
}
      
      



, , SOLID , Dependency injection. . . , - KISS.





“”, , . - . “ ” , ,   . ( - Adapter-, , ).





, “ ”, , . . , . - . - . , , - : . (MVP, MVVM ) , ( -, - …). , . , . . 





“Clean architecture”. . , - . “Clean architecture ”, , , . - . Hello World , ?





, , ( , ). “”. - , .





“ ”

“ ?”, . , , : . , , . . . , , ? - 2 , .. … . - . ? , , “” . . 





. .





- . “ ”, “ ”. “ ” . “ ” - . : - , , .





- . ( ) . , . “” ( /). 





In conclusion, I would like to recall a very useful advice found on the Internet: "Write the code as if it would be read by a maniac who knows where you live." Write good code, and may coffee and cookies come with you. 








All Articles