irrelevant, I'm already an expert
Photo by Aleksey Boev on Unsplash
Classes and objects should have noun or noun phrase names like Customer, WikiPage, Account and AddressParser.
Avoid words like Manager, Processor, Data, or Info in the name of a class.
🤔
/**
* Class summarizing user interest data
*/
data class UserData(
val bookmarkedNewsResources: Set<String>,
val viewedNewsResources: Set<String>,
val followedTopics: Set<String>,
val themeBrand: ThemeBrand,
val darkThemeConfig: DarkThemeConfig,
val useDynamicColor: Boolean,
val shouldHideOnboarding: Boolean,
)
data class AmpLinkInfo(
val ampLink: String,
val destinationUrl: String? = null,
)
package com.squareup.moshi.kotlin.codegen.ksp
private class JsonClassSymbolProcessor(
environment: SymbolProcessorEnvironment,
) : SymbolProcessor {
override fun process(resolver: Resolver): List<KSAnnotated> {
val generatedAnnotation = generatedOption?.let {
AnnotationSpec.builder(ClassName.bestGuess(it))
.addMember("value = [%S]", JsonClassSymbolProcessor::class.java.canonicalName)
.addMember("comments = %S", "https://github.com/square/moshi")
.build()
}
}
internal class OfflineFirstTopicsRepository @Inject constructor(
private val topicDao: TopicDao,
private val network: NiaNetworkDataSource,
) : TopicsRepository {
internal class OfflineFirstNewsRepository @Inject constructor(
private val niaPreferencesDataSource: NiaPreferencesDataSource,
private val newsResourceDao: NewsResourceDao,
private val topicDao: TopicDao,
private val network: NiaNetworkDataSource,
private val notifier: Notifier,
) : NewsRepository {
internal class OfflineFirstTopicsManager @Inject constructor(
private val topicDao: TopicDao,
private val network: NiaNetworkDataSource,
) : TopicsRepository {
internal class OfflineFirstNewsManager @Inject constructor(
private val niaPreferencesDataSource: NiaPreferencesDataSource,
private val newsResourceDao: NewsResourceDao,
private val topicDao: TopicDao,
private val network: NiaNetworkDataSource,
private val notifier: Notifier,
) : NewsRepository {
ForbiddenClassName:
active: true
forbiddenName: [".*Manager"]
class ForbiddenNameDetector : Detector(), SourceCodeScanner {
override fun getApplicableUastTypes(): List<Class<out UElement>> {
return listOf(UClass::class.java)
}
override fun createUastHandler(context: JavaContext): UElementHandler? {
if (isKotlin(context.psiFile?.language)) return null
return object : UElementHandler() {
override fun visitClass(node: UClass) {
val matches = forbidden.filter { it.pattern.matches(node.name) }
for (match in matches) {
context.report(
ISSUE,
context.getLocation(node),
"Name ${node.name} is forbidden for ${match.reason}"
)
}
}
}
}
Executors.newSingleThreadExecutor().asCoroutineDispatcher()
// User.kt
data class User(
val firstName: String,
val lastName: String,
val id: Long
)
// Users.kt
fun User.toLoggableString() = TODO()
// Gold.kt
data class Gold(
val amount: Int,
)
// Golds.kt
fun Gold.toSilver() = amount * 1000
// FauxPas.kt
data class FauxPas(
val context: String,
val action: String
)
❓
/** A spec or type which can be annotated. */
public interface Annotatable {
public val annotations: List<AnnotationSpec>
}
/** A spec which contains documentation. */
public interface Documentable {
public val kdoc: CodeBlock
}
What about something that can have a TypeSpec?
get the garbage bag from under the sink
carry it out to the garage
dump it in the garbage can
fun takeOutGarbage() {
RetrievalStrategyFactory.create(Strategy.WITH_HANDS).retrieve(garbageBag)
Carrier.of(Hand.RIGHT).carryTo(garbageCan)
Dumper.dumpIn(garbageCan)
}
@Composable
private fun SearchTextField(
searchQuery: String,
onSearchQueryChanged: (String) -> Unit,
onSearchTriggered: (String) -> Unit,
) {
val focusRequester = remember { FocusRequester() }
val keyboardController = LocalSoftwareKeyboardController.current
TextField(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
.focusRequester(focusRequester)
.onKeyEvent {
if (it.key == Key.Enter) {
onSearchExplicitlyTriggered()
true
} else {
false
}
}
.testTag("searchTextField"),
shape = RoundedCornerShape(32.dp),
value = searchQuery,
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Search,
),
keyboardActions = KeyboardActions(
onSearch = {
onSearchTriggered()
},
),
maxLines = 1,
singleLine = true,
)
LaunchedEffect(Unit) {
focusRequester.requestFocus()
}
}
obtain
fetch
retrieve
acquire
calculate
generate
There are no solutions, only trade-offs
Thomas Sowell
Classes and objects should have noun or noun phrase names like Customer, WikiPage, Account and AddressParser.
Avoid words like Manager, Processor, Data, or Info in the name of a class.
Getting a good instinct for names is hard. You will have to learn to walk the tightrope between obscurity and verbosity.
Follow the idiomatic names in your language and open source community. For team projects, enforce naming conventions using static analysis to avoid bike-shedding.
At the end of the day, decisions on names are compromises too, so learn to deal with names that aren't perfect.
Debit
Credit
$10,000
$10,000
class PaymentProcessor(private val creditCardServer: CreditCardServer) {
fun processPayment(creditCard: CreditCard, money: Money) {
if (!creditCardServer.isAvailable()) {
throw NotAvailableException()
}
val transaction = creditCardServer.beginTransaction().transaction
val payment = creditCardServer.pay(transaction, creditCard, 500)
if (payment.isOverMaxBalance()) {
throw OverMaxBalanceException()
}
}
}
fun testCreditCardIsCharged() {
val paymentProcessor = PaymentProcessor(mockCreditCardServer)
whenever(mockCreditCardServer.isServerAvailable()).thenReturn(true)
whenever(mockCreditCardServer.beginTransaction()).thenReturn(mockTransactionManager)
whenever(mockTransactionManager.getTransaction()).thenReturn(transaction)
whenever(mockCreditCardServer.pay(transaction, creditCard, 500)).thenReturn(mockPayment)
whenever(mockPayment.isOverMaxBalance()).thenReturn(false)
paymentProcessor.processPayment(creditCard, Money.dollars(500))
verify(mockCreditCardServer).pay(transaction, creditCard, 500)
}
class HappyBirthdayViewModel(
private val userRepository: UserRepository
private val clock: Clock
) {
init {
val birthday = userRepository.user.getField("brthiday")
if (birthday == clock.today()) {
_viewState = _viewState.copy(party = true)
}
}
}
fun testBirthday() {
val user = mock()
whenever(userRepository.user).thenReturn(user)
val viewModel = HappyBirthdayViewModel(userRepository)
verify(user).getField("birthday")
}
fun testCreditCardIsCharged() {
val paymentProcessor = PaymentProcessor(mockCreditCardServer)
whenever(mockCreditCardServer.isServerAvailable()).thenReturn(true)
whenever(mockCreditCardServer.beginTransaction()).thenReturn(mockTransactionManager)
whenever(mockTransactionManager.getTransaction()).thenReturn(transaction)
whenever(mockCreditCardServer.pay(transaction, creditCard, 500)).thenReturn(mockPayment)
whenever(mockPayment.isOverMaxBalance()).thenReturn(false)
paymentProcessor.processPayment(creditCard, Money.dollars(500))
verify(mockCreditCardServer).pay(transaction, creditCard, 500)
}
fun testCreditCardIsCharged() {
paymentProcessor = PaymentProcessor(fakeCreditCardServer)
paymentProcessor.processPayment(creditCard, Money.dollars(500))
assertEquals(500, fakeCreditCardServer.getMostRecentCharge(creditCard))
}
replace just this with an in-memory database
applicability
fidelity
brittle
complex
Photo by Markus Spiske on Unsplash
Premature optimization is the root of all evil
We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.
Yet we should not pass up our opportunities in that critical 3%. A good programmer will not be lulled into complacency by such reasoning, he will be wise to look carefully at the critical code; but only after that code has been identified
Structured Programming with go to Statements, ACM Journal Computing Surveys, Vol 6, No. 4, Dec. 1974
Photo by Edho Pratama on Unsplash