Помните собирали такие задания. Но то для серьезных вещей, а у меня более мелкое, близкое нам — кодинг.
Придумать не получалось — а вот когда затребовала среда — оно как бы само нашлось. Просто взял из рабочего проекта задание, которое можно выделить и реализовать без какой-либо связи с проектом.
Почему это не тривиально? Т.к. либо слишком простое придумывается (на что есть готовый ответ в сети), либо слишком сложное, для которого нужно погружать в детали проекта. А тут как бы вещь в себе, но при этом ответа в сети готового не найдете. Код компилируется, не требует зависимостей от библиотек.
Итак, полный текст, который можно скопировать и дать GPT:
Для GPT
Язык Dart. Требуется реализовать методы retrieveItems и getItemFromCache менеджера кеширования — класс ObjectDdCacheManager.
Условия:
1. Учитывать FetchDataPolicy — если never, то запрос к API не выполняем, возвращаем то что есть в кеше (или ничего). Если onEmptyCache — то делаем запрос к API только в случае, если в кеше нет данных. Если always — то всегда делаем запрос к API и обновляем данные кеша.
2. Учитывать CacheUpdateMode. Если entities — то только заменяем сущности. Если box — то удаляем старую коробку целиком со всеми объектами.
3. Учитывать фильтр Filter. Если установлен, то запрос к базе делаем через getBeyKey (вид фильтра entityKey) или select (вид range). Фильтр применяем и для удаления сущностей в режиме entities — удаляем все сущности из коробки, которые соответствуют фильтру (а если фильтр не установлен — то просто заменяем сущности методом putAll).
4. Учитывать флаг useMemoryCache. Если установлен, то кроме базы данных используем дополнительно и кеш в памяти — _memoryCache. С ним работаем так же как с базой (можно добавить обертку по аналогии с моим моком ObjectDdMock). Если данные есть в _memoryCache и не нужно обновлять данные — в базу не лезем.
Для удобства, чтобы код собирался, добавлен раздел с моками — его трогать не нужно.
// ****************************** MOCKS ******************************class ErrorInfo {
final int errorCode;
ErrorInfo(this.errorCode);
}
class ResultOrError<TResult> {
late bool isSuccess;
TResult? result;
ErrorInfo? error;
ResultOrError._fromResult(this.result) {
isSuccess = true;
error = null;
}
ResultOrError._fromError(this.error) {
isSuccess = false;
result = null;
}
factory ResultOrError.fromResult(TResult result) =>
ResultOrError._fromResult(result);
factory ResultOrError.fromError(ErrorInfo error) =>
ResultOrError._fromError(error);
}
class ApiObject1 {
final Map<String, String> items = {"item1": "value1", "item2": "value2"};
}
abstract class DataEntity {
String get key;
}
class DataEntity1 extends DataEntity {
String name;
String value;
@override
String get key => name;
DataEntity1(this.name, this.value);
}
class Model1 {
final String item;
Model1(this.item);
}
class ObjectDdMock<TDataEntity extends DataEntity> {
String get boxKey => TDataEntity.runtimeType.toString();
static final Map<String, Map<String, dynamic>> _boxes = {};
List<TDataEntity> getAll() {
return _boxes[boxKey]?.values.cast<TDataEntity>().toList() ?? [];
}
TDataEntity? getBeyKey(String key) {
final box = _boxes[boxKey];
if (null == box) return null;
return box[key] as TDataEntity;
}
List<TDataEntity> select(String startKey, String endKey) {
final box = _boxes[boxKey];
if (null == box) return [];
final allEntities = getAll();
List<String> keys = allEntities
.map((e) => e.key)
.where((k) => k.compareTo(startKey) >= 0 && k.compareTo(endKey) <= 0)
.toList();
keys.sort();
List<TDataEntity> result = [];
for (final key in keys) {
result.add(box[key]!);
}
return result;
}
void putAll(List<TDataEntity> entities) {
for (final entity in entities) {
put(entity);
}
}
void put(TDataEntity entity) {
if (!_boxes.containsKey(boxKey)) {
_boxes[boxKey] = {entity.key: entity};
} else {
_boxes[boxKey]![entity.key] = entity;
}
}
void deleteBox() {
if (_boxes.containsKey(boxKey)) {
_boxes.remove(boxKey);
}
}
void deleteEntities(List<TDataEntity> entities) {
final box = _boxes[boxKey];
if (null != box) {
for (final entity in entities) {
box.remove(entity.key);
}
}
}
}
// ****************************** END MOCKS ******************************enum FetchDataPolicy { never, onEmptyCache, always }
enum CacheUpdateMode { entities, box }
enum FilterKind { entityKey, range }
abstract class Filter {
FilterKind get kind;
}
class FilterByKey implements Filter {
String key;
@override
FilterKind get kind => FilterKind.entityKey;
FilterByKey(this.key);
}
class FilterByRange implements Filter {
String startKey;
String endKey;
@override
FilterKind get kind => FilterKind.range;
FilterByRange({required this.startKey, required this.endKey});
}
abstract class CacheManager<TApiObject, TDataEntity extends DataEntity,
TModel> {
final Future<ResultOrError<TApiObject>> Function() fetchFromApi;
final List<TDataEntity> Function(TApiObject) transformToDataEntities;
final TModel Function(TDataEntity) transformToModel;
final String boxName;
final bool useMemoryCache;
CacheManager(
{required this.fetchFromApi,
required this.transformToDataEntities,
required this.transformToModel,
required this.boxName,
this.useMemoryCache = false});
Future<ResultOrError<List<TModel>>> retrieveItems(
{FetchDataPolicy fetchDataPolicy = FetchDataPolicy.onEmptyCache,
CacheUpdateMode cacheUpdateMode = CacheUpdateMode.entities,
Filter? filter});
Future<TModel?> getItemFromCache(String key);
}
final Map<String, Map<String, dynamic>> _memoryCache = {};
class ObjectDdCacheManager<TApiObject, TDataEntity extends DataEntity, TModel>
extends CacheManager<TApiObject, TDataEntity, TModel> {
ObjectDdCacheManager(
{required super.fetchFromApi,
required super.transformToDataEntities,
required super.transformToModel,
required super.boxName});
@override
Future<ResultOrError<List<TModel>>> retrieveItems(
{FetchDataPolicy fetchDataPolicy = FetchDataPolicy.onEmptyCache,
CacheUpdateMode cacheUpdateMode = CacheUpdateMode.entities,
Filter? filter}) {
// TODO: implement retrieveItemsthrow UnimplementedError();
}
@override
Future<TModel?> getItemFromCache(String key) {
// TODO: implement getItemFromCachethrow UnimplementedError();
}
}
void main() async {
final cacheManager = ObjectDdCacheManager(
fetchFromApi: () async {
return ResultOrError.fromResult(ApiObject1());
},
transformToDataEntities: (apiObject) {
return apiObject.items.keys
.map((k) => DataEntity1(k, apiObject.items[k]!))
.toList();
},
transformToModel: (dataEntity) {
return Model1(dataEntity.value);
},
boxName: 'box1');
final items = await cacheManager.retrieveItems();
print(items.result!.length);
}
Здравствуйте, Shmj, Вы писали:
S>Позже добавлю комменты.
Странно что всем пофиг на тему. Видимо причина — задание не тривиальное и просто лень вникать и читать код за бесплатно.
Выводы мои такие. Вроде o1-preview и Gemini дали рабочий код (хотя в проект я такое добавить не могу и сильно не вчитывался). Gemini даже как будто не просто прошлась по условиям, но и проявила признаки осмысленности.
Конечно, он говнокодистый получился — но ведь такой код не нужно поддерживать — его просто генерят заново по новому запросу.
Основная проблема — пока нет доверия что код нормальный, а разбирать его долго, ввиду плохого качества кода.
Здравствуйте, Shmj, Вы писали:
S>Помните собирали такие задания.
Попросите ИИ пересказать положения закона о какой-либо определенной ситуации, затем соотнесите то, что он выдаст под видом цитат, с текстом соответствующих статей.
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Попросите ИИ пересказать положения закона о какой-либо определенной ситуации, затем соотнесите то, что он выдаст под видом цитат, с текстом соответствующих статей.
Мне интереснее что-то связанное с кодом.
Вот, кстати, начал проверять Google Gemini. Он вроде лучше пишет код без зависимостей от сторонних библиотек, т.к. тупо в них ничего не шарит. А GPT не такой умный, зато все библиотеки досконально знает.
Здравствуйте, Shmj, Вы писали:
S> Вот, кстати, начал проверять Google Gemini. Он вроде лучше пишет код без зависимостей от сторонних библиотек, т.к. тупо в них ничего не шарит. А GPT не такой умный, зато все библиотеки досконально знает.
Хрен он чего знает. Предагал использовать gzread из zlib для чтения заголовка архива После таких предложений ему самое место в стене, о которую он убьется.
Здравствуйте, Shmj, Вы писали:
S>Конечно, он говнокодистый получился — но ведь такой код не нужно поддерживать — его просто генерят заново по новому запросу.
Экспериментировал с Cursor AI.
По запросу генерит просто говнокод.
А когда начинаешь уточнять, через что именно реализовать то-то и то-то, в т.ч. через хелперы в этом же проекте — генерит уже вполне.
Below is one possible reference implementation for the ObjectDdCacheManager class. The key points are:
Respecting the FetchDataPolicy:
never: do not call the API at all.
onEmptyCache: call the API only if the cache (memory or DB) is empty.
always: always call the API.
Respecting the CacheUpdateMode:
entities: delete only the filtered entities (if a filter is provided). Otherwise, just putAll the new ones and do not remove older items that are outside the new batch.
box: delete the entire box, then store the new items.
Respecting Filter:
If FilterByKey, retrieve using getBeyKey.
If FilterByRange, retrieve using select.
If no filter, use getAll.
In entities mode, if filter is set, delete only the matching entities; if no filter is set, simply do putAll.
Respecting useMemoryCache:
If true, we use both _memoryCache and the database.
If data already exists in _memoryCache (and doesn’t need updating), do not hit the DB.
Whenever we write or delete, we do it in both _memoryCache and DB.
getItemFromCache(key): purely local lookup; do not call the API.
class ObjectDdCacheManager<TApiObject, TDataEntity extends DataEntity, TModel>
extends CacheManager<TApiObject, TDataEntity, TModel> {
ObjectDdCacheManager({
required super.fetchFromApi,
required super.transformToDataEntities,
required super.transformToModel,
required super.boxName,
super.useMemoryCache = false,
});
// We'll use this DB mock inside our methods.
final ObjectDdMock<TDataEntity> _db = ObjectDdMock<TDataEntity>();
/// Helper methods for in-memory cache lookups and mutations
Map<String, TDataEntity>? get _memoryBox {
return _memoryCache[boxName]?.cast<String, TDataEntity>();
}
List<TDataEntity> _memoryGetAll() {
if (_memoryBox == null) return [];
return _memoryBox!.values.toList();
}
TDataEntity? _memoryGetByKey(String key) {
return _memoryBox == null ? null : _memoryBox![key];
}
List<TDataEntity> _memorySelect(String startKey, String endKey) {
final all = _memoryGetAll();
final result = all
.where((e) =>
e.key.compareTo(startKey) >= 0 && e.key.compareTo(endKey) <= 0)
.toList();
result.sort((a, b) => a.key.compareTo(b.key));
return result;
}
void _memoryPutAll(List<TDataEntity> entities) {
final box = _memoryCache.putIfAbsent(boxName, () => <String, TDataEntity>{});
for (final entity in entities) {
box[entity.key] = entity;
}
}
void _memoryDeleteBox() {
_memoryCache.remove(boxName);
}
void _memoryDeleteEntities(List<TDataEntity> entities) {
final box = _memoryBox;
if (box != null) {
for (final entity in entities) {
box.remove(entity.key);
}
}
}
/// Retrieve items from the cache according to the filter
List<TDataEntity> _getFromCache(Filter? filter) {
// 1) If `useMemoryCache` is true and memory has data for that box,
// do not access the DB (as per requirement).
// 2) Otherwise, read from DB.
List<TDataEntity> result;
final hasMemory = useMemoryCache && _memoryBox != null && _memoryBox!.isNotEmpty;
if (hasMemory) {
switch (filter?.kind) {
case FilterKind.entityKey:
final key = (filter as FilterByKey).key;
final item = _memoryGetByKey(key);
result = item == null ? [] : [item];
break;
case FilterKind.range:
final range = filter as FilterByRange;
result = _memorySelect(range.startKey, range.endKey);
break;
default:
// No filter
result = _memoryGetAll();
}
return result;
}
// Fallback to DB if memory is not used or empty
switch (filter?.kind) {
case FilterKind.entityKey:
final key = (filter as FilterByKey).key;
final item = _db.getBeyKey(key);
result = item == null ? [] : [item];
break;
case FilterKind.range:
final range = filter as FilterByRange;
result = _db.select(range.startKey, range.endKey);
break;
default:
// No filter
result = _db.getAll();
}
return result;
}
/// Store new entities in the cache/DB according to the update mode + filter
void _storeEntities(
List<TDataEntity> newEntities,
CacheUpdateMode cacheUpdateMode,
Filter? filter,
) {
if (cacheUpdateMode == CacheUpdateMode.box) {
// Delete the entire box from memory and DB, then put all
_db.deleteBox();
if (useMemoryCache) {
_memoryDeleteBox();
}
_db.putAll(newEntities);
if (useMemoryCache) {
_memoryPutAll(newEntities);
}
return;
}
// If we are here, then `cacheUpdateMode == CacheUpdateMode.entities`
if (filter != null) {
// Delete only the matching entities from DB & memory
final oldEntities = _getFromCache(filter);
_db.deleteEntities(oldEntities);
if (useMemoryCache) {
_memoryDeleteEntities(oldEntities);
}
}
// Then put the new ones
_db.putAll(newEntities);
if (useMemoryCache) {
_memoryPutAll(newEntities);
}
}
@override
Future<ResultOrError<List<TModel>>> retrieveItems({
FetchDataPolicy fetchDataPolicy = FetchDataPolicy.onEmptyCache,
CacheUpdateMode cacheUpdateMode = CacheUpdateMode.entities,
Filter? filter,
}) async {
try {
// 1) Check if cache is empty (depending on useMemoryCache).
final cachedData = _getFromCache(filter);
final bool isCacheEmpty = cachedData.isEmpty;
// 2) Decide if we need to fetch from API
bool needApiFetch = false;
switch (fetchDataPolicy) {
case FetchDataPolicy.never:
// never call the API
needApiFetch = false;
break;
case FetchDataPolicy.onEmptyCache:
// call the API only if the cache is empty
needApiFetch = isCacheEmpty;
break;
case FetchDataPolicy.always:
// always call the API
needApiFetch = true;
break;
}
List<TDataEntity> finalEntities = cachedData;
if (needApiFetch) {
final fetchResult = await fetchFromApi();
if (!fetchResult.isSuccess) {
// Return the error if API call failed
return ResultOrError.fromError(fetchResult.error!);
}
// Transform the API object to data entities
final newEntities = transformToDataEntities(fetchResult.result!);
// Write them to DB and/or memory, depending on the update mode
_storeEntities(newEntities, cacheUpdateMode, filter);
// Now re-query the cache to get the final list
finalEntities = _getFromCache(filter);
}
// Convert all TDataEntities to TModels
final models = finalEntities.map(transformToModel).toList();
return ResultOrError.fromResult(models);
} catch (e) {
// In a real system, you'd have better error handling
return ResultOrError.fromError(ErrorInfo(-1));
}
}
@override
Future<TModel?> getItemFromCache(String key) async {
// 1) If useMemoryCache is true, try memory first
if (useMemoryCache) {
final memItem = _memoryGetByKey(key);
if (memItem != null) {
return transformToModel(memItem);
}
}
// 2) Fallback to DB
final dbItem = _db.getBeyKey(key);
if (dbItem != null) {
return transformToModel(dbItem);
}
// 3) Not found at all
return null;
}
}
Explanation of Key Parts
_getFromCache(filter):
Checks in-memory first (if useMemoryCache is set and memory has data).
If memory doesn’t have data (or useMemoryCache is false), queries the database.
Respects the FilterKind — calls either getBeyKey(...), select(...), or getAll().
_storeEntities(newEntities, cacheUpdateMode, filter):
If box mode:
Delete the entire box from both DB and memory, then putAll the new entities.
If entities mode:
If a filter is present, remove only the matching entities from both DB and memory.
putAll the new entities without removing anything else.
This follows the requirement to only replace matching entities in entities mode and fully replace everything in box mode.
retrieveItems handles:
Check if cache is empty: to decide if we need to call the API for onEmptyCache.
Conditionally fetch from API (for onEmptyCache or always); skip for never.
Transform new data to TDataEntity, store them, then read them again and produce List<TModel>.
getItemFromCache(key):
Purely local lookup:
If useMemoryCache, look in _memoryCache.
Otherwise (or if not found in memory), look in DB.
Return null if not found.
With this implementation, you can run the provided main() function and see that items are fetched, stored, and transformed properly. Then you can experiment by changing the policies (FetchDataPolicy), filter, and update modes.
Здравствуйте, Shmj, Вы писали:
S>Странно что всем пофиг на тему. Видимо причина — задание не тривиальное и просто лень вникать и читать код за бесплатно.
Я, например, не знаю этого языка, и оценить нетривиальность кода не могу.
Для реализации методов retrieveItems и getItemFromCache в классе ObjectDdCacheManager, нам нужно учитывать все условия, описанные в задаче. Давайте начнем с реализации этих методов.
Реализация метода retrieveItems
FetchDataPolicy:
never: Возвращаем данные из кеша (или ничего).
onEmptyCache: Делаем запрос к API только если в кеше нет данных.
always: Всегда делаем запрос к API и обновляем данные кеша.
CacheUpdateMode:
entities: Заменяем сущности.
box: Удаляем старую коробку целиком со всеми объектами.
Filter:
Если установлен, то запрос к базе делаем через getBeyKey (вид фильтра entityKey) или select (вид range).
Фильтр применяем и для удаления сущностей в режиме entities.
useMemoryCache:
Если установлен, то используем дополнительно кеш в памяти _memoryCache.
Реализация метода getItemFromCache
useMemoryCache:
Если установлен, то сначала проверяем кеш в памяти _memoryCache.
Если данных нет в _memoryCache, то проверяем базу данных.
code
class ObjectDdCacheManager<TApiObject, TDataEntity extends DataEntity, TModel>
extends CacheManager<TApiObject, TDataEntity, TModel> {
ObjectDdCacheManager(
{required super.fetchFromApi,
required super.transformToDataEntities,
required super.transformToModel,
required super.boxName});
@override
Future<ResultOrError<List<TModel>>> retrieveItems(
{FetchDataPolicy fetchDataPolicy = FetchDataPolicy.onEmptyCache,
CacheUpdateMode cacheUpdateMode = CacheUpdateMode.entities,
Filter? filter}) async {
final box = ObjectDdMock<TDataEntity>();
List<TDataEntity> cachedEntities = [];
// Check memory cache first if useMemoryCache is trueif (useMemoryCache && _memoryCache.containsKey(boxName)) {
cachedEntities = _memoryCache[boxName]!.values.cast<TDataEntity>().toList();
} else {
// Check database cacheif (filter != null) {
if (filter.kind == FilterKind.entityKey && filter is FilterByKey) {
final entity = box.getBeyKey(filter.key);
if (entity != null) {
cachedEntities = [entity];
}
} else if (filter.kind == FilterKind.range && filter is FilterByRange) {
cachedEntities = box.select(filter.startKey, filter.endKey);
}
} else {
cachedEntities = box.getAll();
}
}
// Determine if we need to fetch from APIbool needToFetch = false;
if (fetchDataPolicy == FetchDataPolicy.always) {
needToFetch = true;
} else if (fetchDataPolicy == FetchDataPolicy.onEmptyCache && cachedEntities.isEmpty) {
needToFetch = true;
}
if (needToFetch) {
final apiResult = await fetchFromApi();
if (apiResult.isSuccess) {
final newEntities = transformToDataEntities(apiResult.result!);
if (cacheUpdateMode == CacheUpdateMode.box) {
box.deleteBox();
if (useMemoryCache) {
_memoryCache.remove(boxName);
}
} else if (cacheUpdateMode == CacheUpdateMode.entities) {
if (filter != null) {
List<TDataEntity> entitiesToDelete = [];
if (filter.kind == FilterKind.entityKey && filter is FilterByKey) {
final entity = box.getBeyKey(filter.key);
if (entity != null) {
entitiesToDelete.add(entity);
}
} else if (filter.kind == FilterKind.range && filter is FilterByRange) {
entitiesToDelete = box.select(filter.startKey, filter.endKey);
}
box.deleteEntities(entitiesToDelete);
if (useMemoryCache) {
for (final entity in entitiesToDelete) {
_memoryCache[boxName]?.remove(entity.key);
}
}
}
}
box.putAll(newEntities);
if (useMemoryCache) {
if (!_memoryCache.containsKey(boxName)) {
_memoryCache[boxName] = {};
}
for (final entity in newEntities) {
_memoryCache[boxName]![entity.key] = entity;
}
}
cachedEntities = newEntities;
} else {
return ResultOrError.fromError(apiResult.error!);
}
}
final models = cachedEntities.map(transformToModel).toList();
return ResultOrError.fromResult(models);
}
@override
Future<TModel?> getItemFromCache(String key) async {
if (useMemoryCache && _memoryCache.containsKey(boxName)) {
final entity = _memoryCache[boxName]![key] as TDataEntity?;
if (entity != null) {
return transformToModel(entity);
}
}
final box = ObjectDdMock<TDataEntity>();
final entity = box.getBeyKey(key);
if (entity != null) {
return transformToModel(entity);
}
return null;
}
}
void main() async {
final cacheManager = ObjectDdCacheManager(
fetchFromApi: () async {
return ResultOrError.fromResult(ApiObject1());
},
transformToDataEntities: (apiObject) {
return apiObject.items.keys
.map((k) => DataEntity1(k, apiObject.items[k]!))
.toList();
},
transformToModel: (dataEntity) {
return Model1(dataEntity.value);
},
boxName: 'box1');
final items = await cacheManager.retrieveItems();
print(items.result!.length);
}